def _test_handle_template(self,
                              client_id,
                              redirect_uri,
                              scope,
                              state,
                              encrypted_login,
                              expected_redirect=None,
                              encrypted_access=None,
                              redirect_qparam=None):
        '''This method provides a template for testing handle_grant method from implicit grant provider.'''

        if not encrypted_access:
            encrypted_access = "encrypted access token"

        if not expected_redirect:
            scope = scope or ""
            state = state or ""
            expected_redirect = "%s#access_token=%s&state=%s&token_type=access&expires_in=%s&scope=%s" % \
                                    (redirect_uri, encrypted_access,
                                     urllib.parse.quote(state), self._EXPIRES_IN,
                                     urllib.parse.quote(scope))

        login_token = Token({"user_id": 1})

        access_token = Token({"type": "access"})

        request = Mock()
        request.params = {
            "client_id": client_id,
            "redirect_uri": redirect_uri,
            "scope": scope,
            "state": state,
            "login_token": encrypted_login,
            "redirect": redirect_qparam
        }

        self._tokens_service.decrypt = Mock(return_value=login_token)
        self._tokens_service.generate = Mock(return_value=access_token)
        self._tokens_service.encrypt = Mock(return_value=encrypted_access)

        response = self._handler.handle_grant(request)

        if not redirect_qparam:
            self.assertIsInstance(response, RedirectResponse)
        else:
            self.assertIsInstance(response, Response)

        self.assertEqual(expected_redirect, response.headers.get("Location"))

        self._tokens_service.decrypt.assert_called_once_with(encrypted_login)
        self._tokens_service.validate.assert_called_once_with(login_token)
        self._tokens_service.generate.assert_called_once_with(
            {
                "client_id": client_id,
                "user_id": login_token.user_id,
                "scopes": scope,
                "expires_in": self._EXPIRES_IN
            }, TokenGeneratorFactory.ACCESS_TOKEN)
        self._tokens_service.encrypt.assert_called_once_with(
            access_token, client_id)
    def _test_validate_security_context_template(self,
                                                 valid=None,
                                                 side_effect=None):
        '''This method provides a template for checking various behaviors of validate_security_context method.'''

        access_token = Token({})

        kwargs = {}

        if side_effect:
            kwargs["side_effect"] = side_effect
        else:
            kwargs["return_value"] = valid

        request = Mock()
        request.context = Mock()
        request.context.security = Mock()
        request.context.security.access_token = access_token
        request.context.security.validate_context = Mock(**kwargs)

        self.assertEqual(
            access_token,
            self._controller.validate_security_context(request,
                                                       attr_scope="scopes"))

        request.context.security.validate_context.assert_called_once_with(
            "scopes")
示例#3
0
    def _test_authenticate_encrypt_ex(self, ex):
        '''This method provides a template test case which allows token encrypt method to raise various exceptions.'''

        creation_time, expiration_time = self._mock_creationexpiration_time()

        user = User(username="******", password="******")
        user.session = Mock()

        return_url = "/test/url"

        token = Token({
            "client_id": self._IDP_CLIENTID,
            "type": "login",
            "user_id": user.user_id,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        request, user_repo_cls, user_repo, tokens_service_cls, \
            tokens_service, clienturl_facade = self._mock_authenticate_dependencies(token, user, return_url)

        tokens_service.encrypt = Mock(side_effect=ex)

        self._idp_controller.authenticate(
            request,
            tokens_service_cls=tokens_service_cls,
            user_repo_cls=user_repo_cls)
    def generate(self, token_desc, time_provider=time):
        '''This method generates a login token. In order to succeed token descriptor must contain the following keys:

            * client_id - a unique identifier for the idp which generated the token.
            * user_id - idp user unique identifier.
            * expires_in - an integer value in seconds determining the maximum validity of the token.

        If any of the above keys are missing an oauth 2 exception is raised.
        '''

        token_desc = token_desc or {}

        client_id = self._validate_missing_attr("client_id", token_desc)
        user_id = self._validate_missing_attr("user_id", token_desc)

        creation_time = int(time_provider.time())

        expires_in = self._validate_missing_attr("expires_in", token_desc)

        expiration_time = creation_time + expires_in

        token = {
            "client_id": client_id,
            "type": self.TOKEN_TYPE,
            "user_id": user_id,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        }

        return Token(token)
    def generate(self, token_desc, time_provider=time):
        '''This method generates a new access token starting from the givent token descriptor. In order to succeed the token
        descriptor must contain the following keys:

            * client_id - Client unique identifier.
            * user_id - User unique identifier.
            * scopes - The scopes requested for this client (a space delimited list of strings).
            * expires_in - The time to live period (in seconds) for the newly generated access token.
        '''

        token_desc = token_desc or {}

        client_id = self._validate_missing_attr("client_id", token_desc)
        user_id = self._validate_missing_attr("user_id", token_desc)
        scopes = self._validate_missing_attr("scopes", token_desc).split(" ")
        expires_in = self._validate_missing_attr("expires_in", token_desc)

        client = self._validate_client(client_id)
        self._validate_client_scopes(client.scopes, scopes)

        creation_time = time_provider.time()

        expiration_time = int(creation_time) + expires_in

        token = Token({
            "client_id": client_id,
            "type": "access",
            "user_id": user_id,
            "scopes": scopes,
            "creation_time": int(creation_time),
            "expiration_time": expiration_time
        })

        return token
    def test_validate_context_ok_noscopes(self):
        '''This test case ensures a security context is valid when no required scopes are necessary.'''

        access_token = Token({"scopes": ["scope1"]})
        security_ctx = SecurityContext(access_token)

        self.assertTrue(security_ctx.validate_context())
示例#7
0
    def test_aes_encrypt_unexpected_error(self):
        '''This test case ensures all unexpected exceptions from encrypt are converted correctly into OAuth2 encryption errors.'''

        aes_provider = AesTokenEncryption()

        with self.assertRaises(OAuth2TokenEncryptionError):
            aes_provider.encrypt_token(Token({}), "Simple IV", "Simple Key")
示例#8
0
    def test_authenticate_different_passwords(self):
        '''This test case ensures an exception is raised when passwords do not match.'''

        creation_time, expiration_time = self._mock_creationexpiration_time()

        user = User(username="******", password="******")
        user.session = Mock()

        return_url = "/test/url"

        token = Token({
            "client_id": self._IDP_CLIENTID,
            "type": "login",
            "user_id": user.user_id,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        request, user_repo_cls, user_repo, tokens_service_cls, \
            tokens_service, clienturl_facade = self._mock_authenticate_dependencies(token, user, return_url)

        with self.assertRaises(OAuth2AuthenticationError):
            self._idp_controller.authenticate(
                request,
                tokens_service_cls=tokens_service_cls,
                user_repo_cls=user_repo_cls)
    def test_encrypt_invalidclient(self):
        '''This test case ensures all exceptions occuring during client load are converted to oauth2 invalid client exceptions.'''

        self._client_repo.load = Mock(
            side_effect=Exception("Unexpected exception."))

        with self.assertRaises(OAuth2InvalidClientError):
            self._tokens_service.encrypt(Token({}), "mock-client")
示例#10
0
    def test_aes_descrypt_notokeniv(self):
        '''This test case ensures aes decrypt fails if no token iv is given.'''

        aes_provider = AesTokenEncryption()

        with self.assertRaises(OAuth2InvalidTokenDescriptorError) as ctx:
            aes_provider.decrypt_token(Token({}), None, None)

        self.assertEqual("token_iv", ctx.exception.attr_name)
示例#11
0
    def test_aes_encrypt_notokenkey(self):
        '''This test case ensures aes encrypt fails if no token_key is given.'''

        aes_provider = AesTokenEncryption()

        with self.assertRaises(OAuth2InvalidTokenDescriptorError) as ctx:
            aes_provider.encrypt_token(Token({}), "simple key", None)

        self.assertEqual("token_key", ctx.exception.attr_name)
    def test_middleware_ok_header(self):
        '''This test case ensures OAuth2TokensMiddleware executes correctly when configured according to spec (runs after all native
        Fantastico middlewares executed) and access token is sent in header **Authorization**.'''

        param_token = "encrypted token value"
        token = Token({"scopes": ["scope1", "scope2"]})

        self._request.params = {}
        self._request.headers = {"Authorization": "Bearer %s" % param_token}

        self._test_middleware_template(param_token, token)
    def test_validate_context_invalid(self):
        '''This test case ensures a security context is invalid when required scopes are not found in access_token scopes.'''

        for attr_scope in self._ATTR_SCOPES:
            access_token = Token({"scopes": []})
            required_scopes = ["scope1", "scope2"]
            required_scopes_obj = self._mock_required_scopes_obj(
                attr_scope, required_scopes)

            security_ctx = SecurityContext(access_token, required_scopes_obj)
            self.assertFalse(security_ctx.validate_context(attr_scope))
示例#14
0
    def test_invalidate_generator_ex(self):
        '''This test case ensures generator unexpected exceptions are converted to oauth2 concrete exceptions.'''

        token = Token({"type": "mock-type"})

        ex = Exception("Unexpected exception.")

        self._tokens_generator.invalidate = Mock(side_effect=ex)

        with self.assertRaises(OAuth2InvalidTokenTypeError):
            self._tokens_service.invalidate(token)
    def test_middleware_ok_query(self):
        '''This test case ensures OAuth2TokensMiddleware executes correctly when configured according to spec (runs after all native
        Fantastico middlewares executed) and access token is sent in query parameter **token**.'''

        param_token = "encrypted token value"
        token = Token({"scopes": ["scope1", "scope2"]})

        self._request.params = {OAuth2TokensMiddleware.TOKEN_QPARAM: param_token}
        self._request.headers = {}

        self._test_middleware_template(param_token, token)
示例#16
0
    def test_invalidate_ok(self):
        '''This test case ensures tokens can be invalidated successfully.'''

        token = Token({"type": "mock-type"})

        self._tokens_generator.invalidate = Mock()

        self.assertIsNone(self._tokens_service.invalidate(token))

        self._tokens_factory.get_generator.assert_called_once_with(
            token.type, self._db_conn)
        self._tokens_generator.invalidate.assert_called_once_with(token)
    def test_required_scopes_unauthorized(self):
        '''This test case ensures a concrete exception is raised if the requested scopes are not available in
        the current security context.'''
        
        access_token = Token({"scopes": []})

        request = Mock()
        request.context = Mock()
        request.context.security = SecurityContext(access_token)
        
        mock_controller = MockController(Mock())
        
        self.assertRaises(OAuth2UnauthorizedError, lambda: mock_controller.do_stuff(request))
示例#18
0
    def test_invalidate_generator_oauth2ex(self):
        '''This test case ensures generator oauth2 exceptions are bubbled up.'''

        token = Token({"type": "mock-type"})

        ex = OAuth2Error(error_code=-1)

        self._tokens_generator.invalidate = Mock(side_effect=ex)

        with self.assertRaises(OAuth2Error) as ctx:
            self._tokens_service.invalidate(token)

        self.assertEqual(ex, ctx.exception)
    def test_validate_token_ok(self):
        '''This test case ensures a valid token does not throw an exception.'''

        creation_time = time.time()
        expiration_time = int(creation_time + 300)

        token = Token({
            "client_id": "test-idp",
            "type": LoginTokenGenerator.TOKEN_TYPE,
            "user_id": 1,
            "creation_time": int(creation_time),
            "expiration_time": expiration_time
        })

        self.assertTrue(self._generator.validate(token))
    def test_validate_token_expired(self):
        '''This test case ensures a token which is expired fails validation of login token generator.'''

        creation_time = int(time.time() - 7200)
        expiration_time = int(creation_time - 300)

        token = Token({
            "client_id": "test-idp",
            "type": LoginTokenGenerator.TOKEN_TYPE,
            "user_id": 1,
            "creation_time": int(creation_time),
            "expiration_time": expiration_time
        })

        self.assertRaises(OAuth2TokenExpiredError,
                          lambda: self._generator.validate(token))
    def test_validate_tokenexpired(self):
        '''This test case ensures an exception is raised if the token is expired.'''

        creation_time = int(time.time() - 7200)
        expiration_time = int(time.time() - 3600)

        token = Token({
            "client_id": "sample-app",
            "type": "access",
            "user_id": 1,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        with self.assertRaises(OAuth2TokenExpiredError):
            self._generator.validate(token)
示例#22
0
    def test_validate_ok(self):
        '''This test case ensures token validation works correctly for a given token.'''

        token_desc = {
            "client_id": "abc",
            "type": "mock-type",
            "attr": "test-attr"
        }
        token = Token(token_desc)

        self._tokens_generator.validate = Mock(return_value=True)

        self.assertTrue(self._tokens_service.validate(token))

        self._tokens_factory.get_generator.assert_called_once_with(
            token.type, self._db_conn)
        self._tokens_generator.validate.assert_called_once_with(token)
示例#23
0
    def test_generate_ok(self):
        '''This test case ensures tokens service can generate a token starting from a given dictionary.'''

        token_type = "mock-type"
        token_desc = {"client_id": "abc", "test_attr": 1}

        expected_token = Token(token_desc)

        self._tokens_generator.generate = Mock(return_value=expected_token)

        token = self._tokens_service.generate(token_desc, token_type)

        self.assertEqual(expected_token, token)

        self._tokens_factory.get_generator.assert_called_once_with(
            token_type, self._db_conn)
        self._tokens_generator.generate.assert_called_once_with(token_desc)
示例#24
0
    def _test_authenticate_ok(self, return_url, expected_url):
        '''This method provides a template test case for ensuring authenticate succeeds for various return_url values.'''

        user = User(username="******",
                    password="******",
                    person_id=1)
        user.user_id = 123

        creation_time, expiration_time = self._mock_creationexpiration_time()

        token = Token({
            "client_id": self._IDP_CLIENTID,
            "type": "login",
            "user_id": user.user_id,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        request, user_repo_cls, user_repo, tokens_service_cls, \
            tokens_service, clienturl_facade = self._mock_authenticate_dependencies(token, user, return_url)

        response = self._idp_controller.authenticate(
            request,
            tokens_service_cls=tokens_service_cls,
            user_repo_cls=user_repo_cls)

        self.assertIsNotNone(response)
        self.assertEqual(302, response.status_code)

        location = response.headers.get("Location")

        self.assertEqual(expected_url, location)

        user_repo.load_by_username.assert_called_once_with(user.username)
        self._hasher.hash_password.assert_called_once_with(
            user.password, DictionaryObject({"salt": user.user_id}))

        tokens_service_cls.assert_called_once_with(clienturl_facade.session)
        tokens_service.generate.assert_called_once_with(
            {
                "client_id": self._IDP_CLIENTID,
                "user_id": user.user_id,
                "expires_in": self._EXPIRES_IN
            }, TokenGeneratorFactory.LOGIN_TOKEN)
        tokens_service.encrypt.assert_called_once_with(token, token.client_id)
    def test_validate_token_invalidtype(self):
        '''This test case ensures a token with a different type than login can not be validated by login token generator.'''

        creation_time = time.time()
        expiration_time = int(creation_time + 300)

        token = Token({
            "client_id": "test-idp",
            "type": "Unknown",
            "user_id": 1,
            "creation_time": int(creation_time),
            "expiration_time": expiration_time
        })

        with self.assertRaises(OAuth2InvalidTokenTypeError) as ctx:
            self._generator.validate(token)

        self.assertEqual(token.type, ctx.exception.token_type)
    def _test_required_scopes_method(self, method, expected_scopes):
        '''This method provides a template test case for invoking and asserting result of a method decorated with
        @RequiredScopes.'''

        expected_scopes = sorted(expected_scopes)

        access_token = Token({"scopes": expected_scopes})

        request = Mock()
        request.context = Mock()
        request.context.security = SecurityContext(access_token)

        self.assertIsNone(method(request))

        security_ctx = request.context.security

        self.assertEqual(access_token, security_ctx.access_token)
        self.assertEqual(expected_scopes, security_ctx.required_scopes.scopes)
示例#27
0
    def test_aes_ok(self):
        '''This test case ensures token encryption correctly supports AES-128 algorithm.'''

        aes_provider = AesTokenEncryption()

        for key_length in [16, 24, 32]:
            token_key = Random.new().read(key_length)

            token = Token({"client_id": "test client", "attr1": "test"})

            token_encrypted = aes_provider.encrypt_token(
                token, self._aes_iv, token_key)
            token_decrypted = aes_provider.decrypt_token(
                token_encrypted, self._aes_iv, token_key)

            self.assertIsNotNone(token_decrypted)
            self.assertEqual(token.client_id, token_decrypted.client_id)
            self.assertEqual(token.attr1, token_decrypted.attr1)
    def test_validate_ok(self):
        '''This test case ensures a valid token passes validation.'''

        creation_time = int(time.time())
        expiration_time = creation_time + 3600

        token = Token({
            "client_id": "sample-app",
            "type": "access",
            "user_id": 1,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        expected_client = Client(client_id=token.client_id, revoked=False)
        self._mock_client_search(expected_client)

        self.assertTrue(self._generator.validate(token))
    def test_validate_invalidtype(self):
        '''This test case ensures an exception is raised when the token type is not compatible with generator.'''

        creation_time = int(time.time())
        expiration_time = creation_time + 3600

        token = Token({
            "client_id": "sample-app",
            "type": "unknown",
            "user_id": 1,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        with self.assertRaises(OAuth2InvalidTokenTypeError) as ctx:
            self._generator.validate(token)

        self.assertEqual(token.type, ctx.exception.token_type)
    def test_validate_token_clientrevoked(self):
        '''This test case ensures an expection is raised if the token client is not valid.'''

        creation_time = int(time.time())
        expiration_time = creation_time + 3600

        token = Token({
            "client_id": "sample-app",
            "type": "access",
            "user_id": 1,
            "creation_time": creation_time,
            "expiration_time": expiration_time
        })

        expected_client = Client(client_id=token.client_id, revoked=True)
        self._mock_client_search(expected_client)

        with self.assertRaises(OAuth2InvalidClientError):
            self._generator.validate(token)