def test_is_enabled(): conf1 = { 'user_authentication': { 'oauth': { 'enabled': False } }, 'keys': { 'secret': None, 'private_key_path': None, 'public_key_path': None } } with pytest.raises(OauthNotConfiguredError): token_manager(conf1) conf1 = { 'user_authentication': { 'oauth': { 'enabled': True, 'default_token_expiration': 1000 }, 'keys': { 'secret': None, 'private_key_path': None, 'public_key_path': None } } } with pytest.raises(InvalidOauthConfigurationError): token_manager(conf1) conf1 = { 'user_authentication': { 'oauth': { 'enabled': True, 'default_token_expiration': 'blah' }, 'keys': { 'secret': 'asecret', 'private_key_path': None, 'public_key_path': None } } } with pytest.raises(InvalidOauthConfigurationError): token_manager(conf1) conf1 = { 'user_authentication': { 'oauth': { 'enabled': True, 'default_token_expiration': 1000 }, 'keys': {} } } with pytest.raises(InvalidOauthConfigurationError): token_manager(conf1)
def test_jwt_token(): test_config = {'secret': '123abc456def'} global _token_manager _token_manager = None mgr = token_manager(test_config) t = mgr.generate_token('admin') logger.info('Using token: {}'.format(t)) j = JwtToken(token=t) assert j.credentials == t assert j.identifier == 'admin'
def get_oauth_token(grant_type="password", username=None, password=None, client_id="anonymous"): """ POST /oauth/token Requires the resource-owners credentials in the Authorization Header. This is a bit of a mix of the ResourceOwnerPasswordGrant flow and the ImplicitGrant flow since this function will populate the necessary fields to perform a password grant if the Authorization header is set and no content body is provided Note: the parameters above are embedded within the connexion request object, but must be specified in the method signature in order for connexion to route the request to this method. So it may appear that they are unused, but have no fear, they are! :return: """ # Short-circuit if no oauth/token configured try: tok_mgr = token_manager() authz = ApiRequestContextProxy.get_service()._oauth_app except Exception as e: raise AccessDeniedError("Oauth not enabled in configuration", detail={}) # Add some default properties if not set in the request try: if request.content_length == 0 or not request.form: logger.debug( "Handling converting empty body into form-based grant request") if not request.data and not request.form: setattr( request, "form", ImmutableMultiDict([ ("username", request.authorization.username), ("password", request.authorization.password), ("grant_type", "password"), ("client_id", "anonymous"), ]), ) resp = authz.create_token_response() logger.debug("Token resp: {}".format(resp)) return resp except: logger.debug_exception("Error authenticating") raise
def test_jwt_verifier(): global _token_manager _token_manager = None mgr = token_manager({'secret': 'abc123'}) v = BearerTokenVerifier(settings={}) info = {'jwt': {'credential': 'signature_valid', 'failed_attempts': []}} t = JwtToken.__factory__().generate_token('admin') logger.info('Using token: {}'.format(t)) test_token = JwtToken(token=t) bad_token = JwtToken( token= b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjogImFkbWluIiwgImFjY291bnQiOiAiMTIzIn0.w6gV8ABimGTKth-xiwxtM6v------' ) v.verify_credentials(authc_token=test_token, authc_info=info) with pytest.raises(Exception): v.verify_credentials(authc_token=bad_token, authc_info=info)
def get_oauth_token(): """ POST /oauth/token Requires the resource-owners credentials in the Authorization Header. This is a bit of a mix of the ResourceOwnerPasswordGrant flow and the ImplicitGrant flow since this function will populate the necessary fields to perform a password grant if the Authorization header is set and no content body is provided :return: """ # Short-circuit if no oauth/token configured try: tok_mgr = token_manager() authz = ApiRequestContextProxy.get_service()._oauth_app except Exception as e: raise AccessDeniedError('Oauth not enabled in configuration', detail={}) # Add some default properties if not set in the request try: if request.content_length == 0 or not request.form: logger.debug( 'Handling converting empty body into form-based grant request') if not request.data and not request.form: setattr( request, 'form', ImmutableMultiDict([ ('username', request.authorization.username), ('password', request.authorization.password), ('grant_type', 'password'), ('client_id', 'anonymous') ])) resp = authz.create_token_response() logger.debug('Token resp: {}'.format(resp)) return resp except: logger.debug_exception('Error authenticating') raise
def test_is_enabled(): conf1 = { "user_authentication": {"oauth": {"enabled": False}}, "keys": {"secret": None, "private_key_path": None, "public_key_path": None}, } with pytest.raises(OauthNotConfiguredError): token_manager(conf1) conf1 = { "user_authentication": { "oauth": {"enabled": True, "default_token_expiration": 1000}, "keys": {"secret": None, "private_key_path": None, "public_key_path": None}, } } with pytest.raises(InvalidOauthConfigurationError): token_manager(conf1) conf1 = { "user_authentication": { "oauth": {"enabled": True, "default_token_expiration": "blah"}, "keys": { "secret": "asecret", "private_key_path": None, "public_key_path": None, }, } } with pytest.raises(InvalidOauthConfigurationError): token_manager(conf1) conf1 = { "user_authentication": { "oauth": {"enabled": True, "default_token_expiration": 1000}, "keys": {}, } } with pytest.raises(InvalidOauthConfigurationError): token_manager(conf1)
def test_jwt_token(): test_config = { 'user_authentication': { 'oauth': { 'enabled': True, 'default_token_expiration_seconds': 60 } }, 'keys': { 'secret': 'abc123' } } global _token_manager _token_manager = None mgr = token_manager(test_config) t = mgr.generate_token('admin') logger.info('Using token: {}'.format(t)) j = JwtToken(token=t) assert j.credentials == t assert j.identifier == 'admin'
def test_jwt_token(): test_config = { "user_authentication": { "oauth": { "enabled": True, "default_token_expiration_seconds": 60 } }, "keys": { "secret": "abc123" }, } global _token_manager _token_manager = None mgr = token_manager(test_config) t = mgr.generate_token("admin") logger.info("Using token: {}".format(t)) j = JwtToken(token=t) assert j.credentials == t assert j.identifier == "admin"
def test_jwt_verifier(): global _token_manager _token_manager = None mgr = token_manager({ 'user_authentication': { 'oauth': { 'enabled': True, 'default_token_expiration_seconds': 60 } }, 'keys': { 'secret': 'abc123' } }) v = BearerTokenVerifier(settings={}) # The lookup normally returns the uuid of the user info = { 'authc_info': { 'jwt': { 'credential': 'abc123uuid', 'failed_attempts': [] } } } t, exp = JwtToken.__factory__().generate_token('abc123uuid', return_expiration=True) logger.info('Using token: {}'.format(t)) test_token = JwtToken(token=ensure_str(t)) bad_token = JwtToken( token= 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjogImFkbWluIiwgImFjY291bnQiOiAiMTIzIn0.w6gV8ABimGTKth-xiwxtM6v------' ) v.verify_credentials(authc_token=test_token, authc_info=info) with pytest.raises(Exception): v.verify_credentials(authc_token=bad_token, authc_info=info)
def test_jwt_verifier(): global _token_manager _token_manager = None mgr = token_manager({ "user_authentication": { "oauth": { "enabled": True, "default_token_expiration_seconds": 60 } }, "keys": { "secret": "abc123" }, }) v = BearerTokenVerifier(settings={}) # The lookup normally returns the uuid of the user info = { "authc_info": { "jwt": { "credential": "abc123uuid", "failed_attempts": [] } } } t, exp = JwtToken.__factory__().generate_token("abc123uuid", return_expiration=True) logger.info("Using token: {}".format(t)) test_token = JwtToken(token=ensure_str(t)) bad_token = JwtToken( token= "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjogImFkbWluIiwgImFjY291bnQiOiAiMTIzIn0.w6gV8ABimGTKth-xiwxtM6v------" ) v.verify_credentials(authc_token=test_token, authc_info=info) with pytest.raises(Exception): v.verify_credentials(authc_token=bad_token, authc_info=info)
def generate_token(client, grant_type, user, scope): tok_mgr = token_manager() logger.info('Generating token: {}, {}, {}, {}'.format( client, grant_type, user, scope)) return str(tok_mgr.generate_token(user.username), 'utf-8')
def init_oauth(app): """ Configure the oauth routes and handlers via authlib :return: """ logger.debug('Initializing oauth routes') conf = localconfig.get_config() if not conf.get('oauth') or not conf.get('oauth', {}).get('enabled'): # Not enabled in configuration return None def query_client(client_id): logger.debug('Looking up client: {}'.format(client_id)) # if client_id == 'anonymous': # db = get_session() # c = db.query(OAuth2Client).filter_by(client_id=client_id).first() # logger.debug('Found client: {}'.format(c)) # else: # logger.debug('Using default user client') # # Return a default client to support implicit flows c = OAuth2Client() c.id = 0 c.client_id = client_id c.user_id = client_id c.client_secret = '' c.issued_at = time.time() - 100 c.expires_at = time.time() + 1000 c.grant_type = 'password' c.token_endpoint_auth_method = 'none' c.client_name = 'default' return c def do_not_save_token(token, request): return None def save_token(token, request): logger.debug('Saving token: {}'.format(token)) try: if request.user: user_id = request.user.username else: user_id = None client = request.client tok = OAuth2Token(client_id=client.client_id, user_id=user_id, **token) db = get_session() db.add(tok) db.commit() logger.info('Saved new token') except: logger.exception('Exception saving token') raise try: # Initialize an anonymous client record with session_scope() as db: f = db.query(OAuth2Client).filter_by(client_id='anonymous').first() if not f: c = OAuth2Client() c.client_id = 'anonymous' c.user_id = None c.client_secret = None c.issued_at = time.time() - 100 c.expires_at = time.time() + 1000 c.grant_type = 'password' c.token_endpoint_auth_method = 'none' c.client_name = 'anonymous' db.add(c) except Exception as e: logger.debug('Default client record init failed: {}'.format(e)) app.config['OAUTH2_JWT_ENABLED'] = True app.config['OAUTH2_ACCESS_TOKEN_GENERATOR'] = generate_token app.config['OAUTH2_REFRESH_TOKEN_GENERATOR'] = True # app.config['OAUTH2_TOKEN_EXPIRES_IN'] = { # 'authorization_code': 864000, # 'implicit': 3600, # 'password': int(conf['user_authentication']['oauth'].get('default_token_expiration_seconds')), # 'client_credentials': 864000 # } tok_mgr = token_manager() app.config['OAUTH2_JWT_KEY'] = tok_mgr.default_issuer().signing_key app.config['OAUTH2_JWT_ISS'] = tok_mgr.default_issuer().issuer app.config['OAUTH2_JWT_ALG'] = tok_mgr.default_issuer().signing_alg authz = AuthorizationServer(app, query_client=query_client, save_token=save_token) authz.register_grant(grants.RefreshTokenGrant) authz.register_grant(grants.ImplicitGrant) authz.register_grant(PasswordGrant) return authz
def init_oauth(app, grant_types, expiration_config): """ Configure the oauth routes and handlers via authlib :return: """ logger.debug('Initializing oauth routes') conf = localconfig.get_config() if not conf.get('user_authentication', {}).get('oauth', {}).get('enabled'): # Not enabled in configuration return None def query_client(client_id): logger.debug('Looking up client: {}'.format(client_id)) db = get_session() c = db.query(OAuth2Client).filter_by(client_id=client_id).first() logger.debug('Found client record for client_id: {}'.format(client_id)) return c def do_not_save_token(token, request): return None # Don't use this (yet), due to token signing that allows system to verify without persistence def save_token(token, request): try: if request.user: user_id = request.user.username else: user_id = None client = request.client tok = OAuth2Token(client_id=client.client_id, user_id=user_id, **token) db = get_session() db.add(tok) db.commit() logger.info('Saved new token') except: logger.exception('Exception saving token') raise try: # Initialize an anonymous client record with session_scope() as db: f = db.query(OAuth2Client).filter_by(client_id='anonymous').first() if not f: c = OAuth2Client() c.client_id = 'anonymous' c.user_id = None c.client_secret = None c.issued_at = time.time() - 100 c.expires_at = time.time() + 1000 c.grant_type = 'password' c.token_endpoint_auth_method = 'none' c.client_name = 'anonymous' db.add(c) except Exception as e: logger.debug('Default client record init failed: {}'.format(e)) app.config['OAUTH2_JWT_ENABLED'] = True app.config['OAUTH2_ACCESS_TOKEN_GENERATOR'] = generate_token app.config['OAUTH2_REFRESH_TOKEN_GENERATOR'] = False # Only the password grant type is used, others can stay defaults app.config['OAUTH2_TOKEN_EXPIRES_IN'] = expiration_config tok_mgr = token_manager() app.config['OAUTH2_JWT_KEY'] = tok_mgr.default_issuer().signing_key app.config['OAUTH2_JWT_ISS'] = tok_mgr.default_issuer().issuer app.config['OAUTH2_JWT_ALG'] = tok_mgr.default_issuer().signing_alg authz = AuthorizationServer(app, query_client=query_client, save_token=do_not_save_token) # Support only the password grant for now for grant in grant_types: logger.debug('Registering oauth grant handler: {}'.format( getattr(grant, 'GRANT_TYPE', 'unknown'))) authz.register_grant(grant) logger.debug('Oauth init complete') return authz
def generate_token(client, grant_type, user, scope): tok_mgr = token_manager() return str(tok_mgr.generate_token(user.username), 'utf-8')
def _get_system_user_credentials(self): """ Returns an AccessCredential object representing the system user :return: """ cred = None exp = None # Credential expiration, if needed logger.debug('Loading system user creds') with IdentityManager._cache_lock: cached_cred = IdentityManager._credential_cache.lookup( localconfig.SYSTEM_USERNAME) if cached_cred is not None: if cached_cred.is_expired(): # Flush it logger.debug( 'Cached system credential is expired, flushing') IdentityManager._credential_cache.delete( localconfig.SYSTEM_USERNAME) else: logger.debug('Cached system credential still ok') # Use it cred = cached_cred if cred is None: logger.debug('Doing refresh/initial system cred load') try: tok_mgr = token_manager() except OauthNotConfiguredError: tok_mgr = None # Generate one if tok_mgr: # Generate a token usr = db_account_users.get(localconfig.SYSTEM_USERNAME, session=self.session) system_user_uuid = usr['uuid'] tok, exp = tok_mgr.generate_token(system_user_uuid, return_expiration=True) logger.debug( 'Generated token with expiration {}'.format(exp)) cred = HttpBearerCredential(tok, exp) else: rec = db_accounts.get(localconfig.SYSTEM_USERNAME, session=self.session) usr = db_account_users.get(localconfig.SYSTEM_USERNAME, session=self.session) if not rec or not usr: logger.error( 'Could not find a system account or user. This is not an expected state' ) raise Exception('No system account or user found') # This will not work if the system admin has configured hashed passwords but not oauth. But, that should be caught at config validation. cred = HttpBasicCredential( usr['username'], usr.get('credentials', {}).get(UserAccessCredentialTypes.password, {}).get('value')) if cred is not None: logger.debug('Caching system creds') IdentityManager._credential_cache.cache_it( localconfig.SYSTEM_USERNAME, cred) return cred
def init_oauth(app, grant_types, expiration_config): """ Configure the oauth routes and handlers via authlib :return: """ logger.debug("Initializing oauth routes") try: tok_mgr = token_manager() logger.info("Initialized the token manager") except OauthNotConfiguredError: logger.info("OAuth support not configured, cannot initialize it") return None except InvalidOauthConfigurationError: logger.error("OAuth has invalid configuration, cannot initialize it") raise def query_client(client_id): db = get_session() c = db.query(OAuth2Client).filter_by(client_id=client_id).first() return c def do_not_save_token(token, request): return None # Don't use this (yet), due to token signing that allows system to verify without persistence def save_token(token, request): try: if request.user: user_id = request.user.username else: user_id = None client = request.client tok = OAuth2Token(client_id=client.client_id, user_id=user_id, **token) db = get_session() db.add(tok) db.commit() except: logger.exception("Exception saving token") raise try: # Initialize an anonymous client record with session_scope() as db: f = db.query(OAuth2Client).filter_by(client_id="anonymous").first() if not f: c = OAuth2Client() c.client_id = "anonymous" c.user_id = None c.client_secret = None c.issued_at = time.time() - 100 c.expires_at = time.time() + 1000 c.grant_type = "password" c.token_endpoint_auth_method = "none" c.client_name = "anonymous" db.add(c) except Exception as e: logger.debug("Default client record init failed: {}".format(e)) app.config["OAUTH2_JWT_ENABLED"] = True app.config["OAUTH2_ACCESS_TOKEN_GENERATOR"] = generate_token app.config["OAUTH2_REFRESH_TOKEN_GENERATOR"] = False # Only the password grant type is used, others can stay defaults app.config["OAUTH2_TOKEN_EXPIRES_IN"] = expiration_config app.config["OAUTH2_JWT_KEY"] = tok_mgr.default_issuer().signing_key app.config["OAUTH2_JWT_ISS"] = tok_mgr.default_issuer().issuer app.config["OAUTH2_JWT_ALG"] = tok_mgr.default_issuer().signing_alg authz = AuthorizationServer(app, query_client=query_client, save_token=do_not_save_token) # Support only the password grant for now for grant in grant_types: logger.debug("Registering oauth grant handler: {}".format( getattr(grant, "GRANT_TYPE", "unknown"))) authz.register_grant(grant) logger.debug("Oauth init complete") return authz
def generate_refresh_token(client, user, scope): tok_mgr = token_manager() logger.info('Generating refresh token: {}, {}, {}, {}'.format( client, 'refresh', user, scope)) return str(tok_mgr.generate_token(user.username), 'utf-8')
def init_oauth(app, grant_types, expiration_config): """ Configure the oauth routes and handlers via authlib :param app: :param grant_types: :param expiration_config: :return: """ logger.debug("Initializing oauth routes") try: tok_mgr = token_manager() logger.info("Initialized the token manager") except OauthNotConfiguredError: logger.info("OAuth support not configured, cannot initialize it") return None except InvalidOauthConfigurationError: logger.error("OAuth has invalid configuration, cannot initialize it") raise def query_client(client_id): db = get_session() c = db.query(OAuth2Client).filter_by(client_id=client_id).first() return c def do_not_save_token(token, request): return None # Don't use this (yet), due to token signing that allows system to verify without persistence def save_token(token, request): try: if request.user: user_id = request.user.username else: user_id = None client = request.client tok = OAuth2Token(client_id=client.client_id, user_id=user_id, **token) db = get_session() db.add(tok) db.commit() except: logger.exception("Exception saving token") raise try: expected_metadata = { "token_endpoint_auth_method": "none", # This should be a function of the grant type input but all of our types are this currently "client_name": "anonymous", "grant_types": [grant.GRANT_TYPE for grant in grant_types], } # Initialize an anonymous client record with session_scope() as db: found = (db.query(OAuth2Client).filter_by( client_id=ANONYMOUS_CLIENT_ID).one_or_none()) logger.info("Creating new oauth client record for %s", ANONYMOUS_CLIENT_ID) to_merge = OAuth2Client() to_merge.client_id = ANONYMOUS_CLIENT_ID to_merge.user_id = None to_merge.client_secret = None # These are no-ops effectively since the client isn't authenticated itself to_merge.client_id_issued_at = time.time() - 100 to_merge.client_secret_expires_at = time.time() + 1000 to_merge.set_client_metadata({ "token_endpoint_auth_method": "none", # This should be a function of the grant type input but all of our types are this currently "client_name": ANONYMOUS_CLIENT_ID, "grant_types": [grant.GRANT_TYPE for grant in grant_types], }) merged = setup_oauth_client(found, to_merge) merged = db.merge(merged) logger.info( "Initializing db record for oauth client %s with grants %s", merged.client_id, merged.client_metadata.get("grant_types"), ) except Exception as e: logger.debug("Default client record init failed: {}".format(e)) app.config["OAUTH2_JWT_ENABLED"] = True app.config["OAUTH2_ACCESS_TOKEN_GENERATOR"] = generate_token app.config["OAUTH2_REFRESH_TOKEN_GENERATOR"] = False # Only the password grant type is used, others can stay defaults app.config["OAUTH2_TOKEN_EXPIRES_IN"] = expiration_config app.config["OAUTH2_JWT_KEY"] = tok_mgr.default_issuer().signing_key app.config["OAUTH2_JWT_ISS"] = tok_mgr.default_issuer().issuer app.config["OAUTH2_JWT_ALG"] = tok_mgr.default_issuer().signing_alg authz = AuthorizationServer(app, query_client=query_client, save_token=do_not_save_token) # Support only the password grant for now for grant in grant_types: logger.debug("Registering oauth grant handler: {}".format( getattr(grant, "GRANT_TYPE", "unknown"))) authz.register_grant(grant) logger.debug("Oauth init complete") return authz