Пример #1
0
 def setUp(self):
     """ setup unittest """
     models_mock = MagicMock()
     models_mock.acme_srv.db_handler.DBstore.return_value = FakeDBStore
     modules = {'acme_srv.db_handler': models_mock}
     patch.dict('sys.modules', modules).start()
     import logging
     logging.basicConfig(level=logging.CRITICAL)
     self.logger = logging.getLogger('test_a2c')
     from acme_srv.authorization import Authorization
     self.authorization = Authorization(False, 'http://tester.local', self.logger)
Пример #2
0
def authz(request):
    """ new-authz command """
    if request.method == 'POST' or request.method == 'GET':
        with Authorization(DEBUG, get_url(request.META),
                           LOGGER) as authorization:
            if request.method == 'POST':
                response_dic = authorization.new_post(request.body)
            else:
                response_dic = authorization.new_get(
                    request.build_absolute_uri())
            # create the response
            response = JsonResponse(status=response_dic['code'],
                                    data=response_dic['data'])

            # generate additional header elements
            for element in response_dic['header']:
                response[element] = response_dic['header'][element]

            # logging
            logger_info(LOGGER, request.META['REMOTE_ADDR'],
                        request.META['PATH_INFO'], response_dic)
            # send response
            return response
    else:
        return JsonResponse(status=405,
                            data={
                                'status': 405,
                                'message': 'Method Not Allowed',
                                'detail': 'Wrong request type. Expected POST.'
                            })
Пример #3
0
    def authorizations_invalidate(self, uts=uts_now(), report_format='csv', report_name=None):
        """ authorizations cleanup based on expiry date"""
        self.logger.debug('Housekeeping.authorization_invalidate({0})'.format(uts))

        with Authorization(self.debug, None, self.logger) as authorization:
            # get expired orders
            (field_list, authorization_list) = authorization.invalidate(timestamp=uts)
            # normalize lists
            (field_list, authorization_list) = self._lists_normalize(field_list, authorization_list, 'authorization')
            # convert dates into human readable format
            authorization_list = self._convert_data(authorization_list)

            if report_name:
                if authorization_list:
                    # dump report to file
                    if report_format == 'csv':
                        self.logger.debug('Housekeeping.authorizations_invalidate(): Dump in csv-format')
                        csv_list = self._to_list(field_list, authorization_list)
                        self._csv_dump('{0}.{1}'.format(report_name, report_format), csv_list)
                    elif report_format == 'json':
                        self.logger.debug('Housekeeping.authorizations_invalidate(): Dump in json-format')
                        self._json_dump('{0}.{1}'.format(report_name, report_format), authorization_list)
                    else:
                        self.logger.debug('Housekeeping.authorizations_invalidate():  No dump just return report')
                else:
                    self.logger.debug('Housekeeping.authorizations_invalidate(): No authorizations to dump')
Пример #4
0
def authz(environ, start_response):
    """ authorization handling """
    if environ['REQUEST_METHOD'] == 'POST' or environ['REQUEST_METHOD'] == 'GET':
        with Authorization(DEBUG, get_url(environ), LOGGER) as authorization:
            if environ['REQUEST_METHOD'] == 'POST':
                try:
                    request_body_size = int(environ.get('CONTENT_LENGTH', 0))
                except ValueError:
                    request_body_size = 0
                request_body = environ['wsgi.input'].read(request_body_size)
                response_dic = authorization.new_post(request_body)
            else:
                response_dic = authorization.new_get(get_url(environ, True))

            # generate header and nonce
            headers = [('Content-Type', 'application/json')]
            # enrich header
            if 'header' in response_dic:
                for element, value in response_dic['header'].items():
                    headers.append((element, value))
            start_response('{0} {1}'.format(response_dic['code'], HTTP_CODE_DIC[response_dic['code']]), headers)

            # logging
            logger_info(LOGGER, environ['REMOTE_ADDR'], environ['PATH_INFO'], response_dic)
            return [json.dumps(response_dic['data']).encode('utf-8')]
    else:
        start_response('405 {0}'.format(HTTP_CODE_DIC[405]), [('Content-Type', 'application/json')])
        return [json.dumps({'status':405, 'message':HTTP_CODE_DIC[405], 'detail': 'Wrong request type. Expected POST.'}).encode('utf-8')]
class TestACMEHandler(unittest.TestCase):
    """ test class for ACMEHandler """
    acme = None

    def setUp(self):
        """ setup unittest """
        models_mock = MagicMock()
        models_mock.acme_srv.db_handler.DBstore.return_value = FakeDBStore
        modules = {'acme_srv.db_handler': models_mock}
        patch.dict('sys.modules', modules).start()
        import logging
        logging.basicConfig(level=logging.CRITICAL)
        self.logger = logging.getLogger('test_a2c')
        from acme_srv.authorization import Authorization
        self.authorization = Authorization(False, 'http://tester.local',
                                           self.logger)

    @patch('acme_srv.challenge.Challenge.new_set')
    @patch('acme_srv.authorization.uts_now')
    @patch('acme_srv.authorization.generate_random_string')
    def test_001_authorization__authz_info(self, mock_name, mock_uts,
                                           mock_challengeset):
        """ test Authorization.auth_info() """
        mock_name.return_value = 'randowm_string'
        mock_uts.return_value = 1543640400
        mock_challengeset.return_value = [{'key1': 'value1', 'key2': 'value2'}]
        self.authorization.dbstore.authorization_update.return_value = 'foo'
        self.authorization.dbstore.authorization_lookup.return_value = [{
            'type':
            'identifier_type',
            'value':
            'identifier_value',
            'status__name':
            'foo'
        }]
        self.assertEqual(
            {
                'status': 'foo',
                'expires': '2018-12-02T05:00:00Z',
                'identifier': {
                    'type': 'identifier_type',
                    'value': 'identifier_value'
                },
                'challenges': [{
                    'key2': 'value2',
                    'key1': 'value1'
                }]
            },
            self.authorization._authz_info(
                'http://tester.local/acme/authz/foo'))

    @patch('acme_srv.message.Message.check')
    def test_002_authorization_new_post(self, mock_mcheck):
        """ Authorization.new_post() failed bcs. of failed message check """
        mock_mcheck.return_value = (400, 'message', 'detail', None, None,
                                    'account_name')
        message = '{"foo" : "bar"}'
        self.assertEqual(
            {
                'header': {},
                'code': 400,
                'data': {
                    'detail': 'detail',
                    'message': 'message',
                    'status': 400
                }
            }, self.authorization.new_post(message))

    @patch('acme_srv.authorization.Authorization._authz_info')
    @patch('acme_srv.message.Message.check')
    def test_003_authorization_new_post(self, mock_mcheck, mock_authzinfo):
        """ Authorization.new_post() failed bcs url is missing in protected """
        mock_mcheck.return_value = (200, None, None, 'protected', 'payload',
                                    'account_name')
        mock_authzinfo.return_value = {'authz_foo': 'authz_bar'}
        message = '{"foo" : "bar"}'
        self.assertEqual(
            {
                'header': {},
                'code': 400,
                'data': {
                    'detail': 'url is missing in protected',
                    'message': 'urn:ietf:params:acme:error:malformed',
                    'status': 400
                }
            }, self.authorization.new_post(message))

    @patch('acme_srv.nonce.Nonce.generate_and_add')
    @patch('acme_srv.authorization.Authorization._authz_info')
    @patch('acme_srv.message.Message.check')
    def test_004_authorization_new_post(self, mock_mcheck, mock_authzinfo,
                                        mock_nnonce):
        """ Authorization.new_post() failed bcs url is missing in protected """
        mock_mcheck.return_value = (200, None, None, {
            'url': 'foo_url'
        }, 'payload', 'account_name')
        mock_authzinfo.return_value = {'authz_foo': 'authz_bar'}
        mock_nnonce.return_value = 'new_nonce'
        message = '{"foo" : "bar"}'
        self.assertEqual(
            {
                'header': {
                    'Replay-Nonce': 'new_nonce'
                },
                'code': 200,
                'data': {
                    'authz_foo': 'authz_bar'
                }
            }, self.authorization.new_post(message))

    @patch('acme_srv.challenge.Challenge.new_set')
    @patch('acme_srv.authorization.uts_now')
    @patch('acme_srv.authorization.generate_random_string')
    def test_005_authorization__authz_info(self, mock_name, mock_uts,
                                           mock_challengeset):
        """ test Authorization.auth_info() in case auth_lookup failed """
        mock_name.return_value = 'randowm_string'
        mock_uts.return_value = 1543640400
        mock_challengeset.return_value = [{'key1': 'value1', 'key2': 'value2'}]
        self.authorization.dbstore.authorization_update.return_value = 'foo'
        self.authorization.dbstore.authorization_lookup.return_value = []
        self.assertEqual({},
                         self.authorization._authz_info(
                             'http://tester.local/acme/authz/foo'))

    @patch('acme_srv.nonce.Nonce.generate_and_add')
    @patch('acme_srv.authorization.Authorization._authz_info')
    @patch('acme_srv.message.Message.check')
    def test_006_authorization_new_post(self, mock_mcheck, mock_authzinfo,
                                        mock_nnonce):
        """ Authorization.new_post() failed bcs url is missing in protected """
        mock_mcheck.return_value = (200, None, None, {
            'url': 'foo_url'
        }, 'payload', 'account_name')
        mock_authzinfo.return_value = {}
        mock_nnonce.return_value = 'new_nonce'
        message = '{"foo" : "bar"}'
        self.assertEqual(
            {
                'header': {},
                'code': 403,
                'data': {
                    'detail': 'authorizations lookup failed',
                    'message': 'urn:ietf:params:acme:error:unauthorized',
                    'status': 403
                }
            }, self.authorization.new_post(message))

    def test_007_authorization_invalidate(self):
        """ test Authorization.invalidate() empty authz list """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.return_value = []
        self.assertEqual(([
            'id', 'name', 'expires', 'value', 'created_at', 'token',
            'status__id', 'status__name', 'order__id', 'order__name'
        ], []), self.authorization.invalidate(timestamp))

    def test_008_authorization_invalidate(self):
        """ test Authorization.invalidate() authz with just a name """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.return_value = [
            {
                'name': 'name'
            }
        ]
        self.assertEqual(([
            'id', 'name', 'expires', 'value', 'created_at', 'token',
            'status__id', 'status__name', 'order__id', 'order__name'
        ], []), self.authorization.invalidate(timestamp))

    def test_009_authorization_invalidate(self):
        """ test Authorization.invalidate() authz with a name and non-expirewd status """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.return_value = [
            {
                'name': 'name',
                'status__name': 'foo'
            }
        ]
        self.assertEqual(([
            'id', 'name', 'expires', 'value', 'created_at', 'token',
            'status__id', 'status__name', 'order__id', 'order__name'
        ], [{
            'name': 'name',
            'status__name': 'foo'
        }]), self.authorization.invalidate(timestamp))

    def test_010_authorization_invalidate(self):
        """ test Authorization.invalidate() authz with a name and non-expirewd status """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.return_value = [
            {
                'name': 'name',
                'status__name': 'expired'
            }
        ]
        self.assertEqual(([
            'id', 'name', 'expires', 'value', 'created_at', 'token',
            'status__id', 'status__name', 'order__id', 'order__name'
        ], []), self.authorization.invalidate(timestamp))

    def test_011_authorization_invalidate(self):
        """ test Authorization.invalidate() authz - dbstore.authorization_update() raises an exception """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.return_value = [
            {
                'name': 'name',
                'status__name': 'foo'
            }
        ]
        self.authorization.dbstore.authorization_update.side_effect = Exception(
            'exc_authz_update')
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.authorization.invalidate(timestamp)
        self.assertIn(
            'CRITICAL:test_a2c:acme2certifier database error in Authorization.invalidate(): exc_authz_update',
            lcm.output)

    def test_012_authorization_invalidate(self):
        """ test Authorization.invalidate() cornercase - do not invalidte authorizations with expires 0 """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.return_value = [
            {
                'name': 'name',
                'status__name': 'foo',
                'expires': 0
            }
        ]
        self.assertEqual(([
            'id', 'name', 'expires', 'value', 'created_at', 'token',
            'status__id', 'status__name', 'order__id', 'order__name'
        ], []), self.authorization.invalidate(timestamp))

    def test_013_authorization_invalidate(self):
        """ test Authorization.invalidate() authz - dbstore.authorization_update() raises an exception """
        timestamp = 1596240000
        self.authorization.dbstore.authorizations_expired_search.side_effect = Exception(
            'exc_authz_exp_search')
        # self.authorization.dbstore.authorization_update.side_effect = Exception('exc_authz_update')
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.authorization.invalidate(timestamp)
        self.assertIn(
            'CRITICAL:test_a2c:acme2certifier database error in Authorization.invalidate(): exc_authz_exp_search',
            lcm.output)

    @patch('acme_srv.challenge.Challenge.new_set')
    @patch('acme_srv.authorization.uts_now')
    @patch('acme_srv.authorization.generate_random_string')
    def test_014_authorization__authz_info(self, mock_name, mock_uts,
                                           mock_challengeset):
        """ test Authorization.auth_info() - dbstore.authorization update raises an exception """
        mock_name.return_value = 'randowm_string'
        mock_uts.return_value = 1543640400
        mock_challengeset.return_value = [{'key1': 'value1', 'key2': 'value2'}]
        self.authorization.dbstore.authorization_update.side_effect = Exception(
            'exc_authz_update')
        self.authorization.dbstore.authorization_lookup.return_value = [{
            'name':
            'foo'
        }]
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.authorization._authz_info(
                'http://tester.local/acme/authz/foo')
        self.assertIn(
            'ERROR:test_a2c:acme2certifier database error in Authorization._authz_info(): exc_authz_update',
            lcm.output)

    @patch('acme_srv.challenge.Challenge.new_set')
    @patch('acme_srv.authorization.uts_now')
    @patch('acme_srv.authorization.generate_random_string')
    def test_015_authorization__authz_info(self, mock_name, mock_uts,
                                           mock_challengeset):
        """ test Authorization.auth_info() in case auth_lookup failed """
        mock_name.return_value = 'randowm_string'
        mock_uts.return_value = 1543640400
        mock_challengeset.return_value = [{'key1': 'value1', 'key2': 'value2'}]
        self.authorization.dbstore.authorization_update.return_value = 'foo'
        self.authorization.dbstore.authorization_lookup.side_effect = Exception(
            'exc_acc_lookup')
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.assertEqual({},
                             self.authorization._authz_info(
                                 'http://tester.local/acme/authz/foo'))
        self.assertIn(
            'CRITICAL:test_a2c:acme2certifier database error in Authorization._authz_info(): exc_acc_lookup',
            lcm.output)

    @patch('acme_srv.challenge.Challenge.new_set')
    @patch('acme_srv.authorization.uts_now')
    @patch('acme_srv.authorization.generate_random_string')
    def test_016_authorization__authz_info(self, mock_name, mock_uts,
                                           mock_challengeset):
        """ test Authorization.auth_info() - dbstore.authorization lookup raises an exception """
        mock_name.return_value = 'randowm_string'
        mock_uts.return_value = 1543640400
        mock_challengeset.return_value = [{'key1': 'value1', 'key2': 'value2'}]
        self.authorization.dbstore.authorization_update.return_value = 'foo'
        self.authorization.dbstore.authorization_lookup.side_effect = Exception(
            'exc_authz_update')
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.authorization._authz_info(
                'http://tester.local/acme/authz/foo')
        self.assertIn(
            'CRITICAL:test_a2c:acme2certifier database error in Authorization._authz_info(): exc_authz_update',
            lcm.output)

    @patch('acme_srv.challenge.Challenge.new_set')
    @patch('acme_srv.authorization.uts_now')
    @patch('acme_srv.authorization.generate_random_string')
    def test_017_authorization__authz_info(self, mock_name, mock_uts,
                                           mock_challengeset):
        """ test Authorization.auth_info() - dbstore.authorization lookup raises an exception """
        mock_name.return_value = 'randowm_string'
        mock_uts.return_value = 1543640400
        mock_challengeset.return_value = [{'key1': 'value1', 'key2': 'value2'}]
        self.authorization.dbstore.authorization_update.return_value = 'foo'
        self.authorization.dbstore.authorization_lookup.side_effect = [
            [{
                'type': 'identifier_type',
                'value1': 'identifier_value',
                'status__name': 'foo'
            }],
            Exception('exc_authz_lookup')
        ]
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.authorization._authz_info(
                'http://tester.local/acme/authz/foo')
        self.assertIn(
            'ERROR:test_a2c:acme2certifier database error in Authorization._authz_info(): exc_authz_lookup',
            lcm.output)

    @patch('acme_srv.authorization.Authorization._config_load')
    def test_018__enter__(self, mock_cfg):
        """ test enter """
        mock_cfg.return_value = True
        self.authorization.__enter__()
        self.assertTrue(mock_cfg.called)

    @patch('acme_srv.authorization.load_config')
    def test_019_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        mock_load_cfg.return_value = parser
        self.authorization._config_load()
        self.assertFalse(self.authorization.expiry_check_disable)
        self.assertEqual(86400, self.authorization.validity)

    @patch('acme_srv.authorization.load_config')
    def test_020_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        parser['Authorization'] = {'foo': 'bar'}
        mock_load_cfg.return_value = parser
        self.authorization._config_load()
        self.assertFalse(self.authorization.expiry_check_disable)
        self.assertEqual(86400, self.authorization.validity)

    @patch('acme_srv.authorization.load_config')
    def test_021_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        parser['Authorization'] = {'expiry_check_disable': False}
        mock_load_cfg.return_value = parser
        self.authorization._config_load()
        self.assertFalse(self.authorization.expiry_check_disable)
        self.assertEqual(86400, self.authorization.validity)

    @patch('acme_srv.authorization.load_config')
    def test_022_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        parser['Authorization'] = {'expiry_check_disable': True}
        mock_load_cfg.return_value = parser
        self.authorization._config_load()
        self.assertTrue(self.authorization.expiry_check_disable)
        self.assertEqual(86400, self.authorization.validity)

    @patch('acme_srv.authorization.load_config')
    def test_023_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        parser['Authorization'] = {'validity': 60}
        mock_load_cfg.return_value = parser
        self.authorization._config_load()
        self.assertFalse(self.authorization.expiry_check_disable)
        self.assertEqual(60, self.authorization.validity)

    @patch('acme_srv.authorization.load_config')
    def test_024_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        parser['Authorization'] = {'validity': 'foo'}
        mock_load_cfg.return_value = parser
        with self.assertLogs('test_a2c', level='INFO') as lcm:
            self.authorization._config_load()
        self.assertFalse(self.authorization.expiry_check_disable)
        self.assertEqual(86400, self.authorization.validity)
        self.assertIn(
            'WARNING:test_a2c:Authorization._config_load(): failed to parse validity: foo',
            lcm.output)

    @patch('acme_srv.authorization.load_config')
    def test_025_config_load(self, mock_load_cfg):
        """ test _config_load """
        parser = configparser.ConfigParser()
        parser['Directory'] = {'url_prefix': 'url_prefix'}
        mock_load_cfg.return_value = parser
        self.authorization._config_load()
        self.assertFalse(self.authorization.expiry_check_disable)
        self.assertEqual(86400, self.authorization.validity)
        self.assertEqual({'authz_path': 'url_prefix/acme/authz/'},
                         self.authorization.path_dic)

    @patch('acme_srv.authorization.Authorization._authz_info')
    def test_026_new_get(self, mock_info):
        """ new get """
        mock_info.return_value = 'foo'
        result = {'code': 200, 'data': 'foo', 'header': {}}
        self.assertEqual(result, self.authorization.new_get('url'))