mirror of
https://github.com/sartography/spiffworkflow-backend.git
synced 2025-02-24 13:28:31 +00:00
ran precommit stuff
This commit is contained in:
parent
fea30d0a16
commit
25c6e74a90
@ -24,7 +24,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Authentication
|
- Authentication
|
||||||
responses:
|
responses:
|
||||||
'200':
|
"200":
|
||||||
description: Redirects to authentication server
|
description: Redirects to authentication server
|
||||||
/login_return:
|
/login_return:
|
||||||
parameters:
|
parameters:
|
||||||
@ -49,7 +49,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Authentication
|
- Authentication
|
||||||
responses:
|
responses:
|
||||||
'200':
|
"200":
|
||||||
description: Test Return Response
|
description: Test Return Response
|
||||||
/logout:
|
/logout:
|
||||||
parameters:
|
parameters:
|
||||||
@ -70,7 +70,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Authentication
|
- Authentication
|
||||||
responses:
|
responses:
|
||||||
'200':
|
"200":
|
||||||
description: Logout Authenticated User
|
description: Logout Authenticated User
|
||||||
/logout_return:
|
/logout_return:
|
||||||
get:
|
get:
|
||||||
@ -80,7 +80,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Authentication
|
- Authentication
|
||||||
responses:
|
responses:
|
||||||
'200':
|
"200":
|
||||||
description: Logout Authenticated User
|
description: Logout Authenticated User
|
||||||
|
|
||||||
/login_swagger:
|
/login_swagger:
|
||||||
@ -110,7 +110,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Authentication
|
- Authentication
|
||||||
responses:
|
responses:
|
||||||
'304':
|
"304":
|
||||||
description: Redirection to the hosted frontend with an auth_token header.
|
description: Redirection to the hosted frontend with an auth_token header.
|
||||||
|
|
||||||
/status:
|
/status:
|
||||||
|
@ -14,7 +14,11 @@ CORS_ALLOW_ORIGINS = re.split(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Keycloak server
|
# Keycloak server
|
||||||
KEYCLOAK_SERVER_URL = environ.get("KEYCLOAK_SERVER_URL", default="http://localhost:7002")
|
KEYCLOAK_SERVER_URL = environ.get(
|
||||||
|
"KEYCLOAK_SERVER_URL", default="http://localhost:7002"
|
||||||
|
)
|
||||||
KEYCLOAK_CLIENT_ID = environ.get("KEYCLOAK_CLIENT_ID", default="spiffworkflow-backend")
|
KEYCLOAK_CLIENT_ID = environ.get("KEYCLOAK_CLIENT_ID", default="spiffworkflow-backend")
|
||||||
KEYCLOAK_REALM_NAME = environ.get("KEYCLOAK_REALM_NAME", default="spiffworkflow")
|
KEYCLOAK_REALM_NAME = environ.get("KEYCLOAK_REALM_NAME", default="spiffworkflow")
|
||||||
KEYCLOAK_CLIENT_SECRET_KEY = environ.get("KEYCLOAK_CLIENT_SECRET_KEY", default="JXeQExm0JhQPLumgHtIIqf52bDalHz0q") # noqa: S105
|
KEYCLOAK_CLIENT_SECRET_KEY = environ.get(
|
||||||
|
"KEYCLOAK_CLIENT_SECRET_KEY", default="JXeQExm0JhQPLumgHtIIqf52bDalHz0q"
|
||||||
|
) # noqa: S105
|
||||||
|
@ -12,9 +12,7 @@ def find_or_create_user(username: str = "test_user1") -> Any:
|
|||||||
"""Find_or_create_user."""
|
"""Find_or_create_user."""
|
||||||
user = UserModel.query.filter_by(username=username).first()
|
user = UserModel.query.filter_by(username=username).first()
|
||||||
if user is None:
|
if user is None:
|
||||||
user = UserModel(username=username,
|
user = UserModel(username=username, service="local", service_id=username)
|
||||||
service='local',
|
|
||||||
service_id=username)
|
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
"""User."""
|
"""User."""
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
import marshmallow
|
import marshmallow
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask_bpmn.api.api_error import ApiError
|
|
||||||
from flask_bpmn.models.db import db
|
from flask_bpmn.models.db import db
|
||||||
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
|
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
|
||||||
from marshmallow import Schema
|
from marshmallow import Schema
|
||||||
@ -18,11 +15,7 @@ class UserModel(SpiffworkflowBaseDBModel):
|
|||||||
"""UserModel."""
|
"""UserModel."""
|
||||||
|
|
||||||
__tablename__ = "user"
|
__tablename__ = "user"
|
||||||
__table_args__ = (
|
__table_args__ = (db.UniqueConstraint("service", "service_id", name="service_key"),)
|
||||||
db.UniqueConstraint(
|
|
||||||
"service", "service_id", name="service_key"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
username = db.Column(db.String(50), nullable=False, unique=True)
|
username = db.Column(db.String(50), nullable=False, unique=True)
|
||||||
uid = db.Column(db.String(50), unique=True)
|
uid = db.Column(db.String(50), unique=True)
|
||||||
@ -57,7 +50,7 @@ class UserModel(SpiffworkflowBaseDBModel):
|
|||||||
# 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=hours, minutes=0, seconds=0),
|
# 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=hours, minutes=0, seconds=0),
|
||||||
# 'iat': datetime.datetime.utcnow(),
|
# 'iat': datetime.datetime.utcnow(),
|
||||||
"sub": f"service:{self.service}::service_id:{self.service_id}",
|
"sub": f"service:{self.service}::service_id:{self.service_id}",
|
||||||
"token_type": "internal"
|
"token_type": "internal",
|
||||||
}
|
}
|
||||||
return jwt.encode(
|
return jwt.encode(
|
||||||
payload,
|
payload,
|
||||||
@ -71,11 +64,12 @@ class UserModel(SpiffworkflowBaseDBModel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_open_id_user_info(cls, user_info):
|
def from_open_id_user_info(cls, user_info):
|
||||||
|
"""From_open_id_user_info."""
|
||||||
instance = cls()
|
instance = cls()
|
||||||
instance.service = 'keycloak'
|
instance.service = "keycloak"
|
||||||
instance.service_id = user_info['sub']
|
instance.service_id = user_info["sub"]
|
||||||
instance.name = user_info['preferred_username']
|
instance.name = user_info["preferred_username"]
|
||||||
instance.username = user_info['sub']
|
instance.username = user_info["sub"]
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@ -1,22 +1,14 @@
|
|||||||
"""APIs for dealing with process groups, process models, and process instances."""
|
"""APIs for dealing with process groups, process models, and process instances."""
|
||||||
from typing import Any
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import current_app
|
|
||||||
from flask import flash
|
from flask import flash
|
||||||
from flask import redirect
|
from flask import redirect
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
from flask_bpmn.models.db import db
|
|
||||||
from werkzeug.wrappers.response import Response
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
from spiffworkflow_backend.models.principal import PrincipalModel
|
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
|
||||||
ProcessInstanceReportModel,
|
|
||||||
)
|
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
|
||||||
from spiffworkflow_backend.services.process_instance_processor import (
|
from spiffworkflow_backend.services.process_instance_processor import (
|
||||||
ProcessInstanceProcessor,
|
ProcessInstanceProcessor,
|
||||||
)
|
)
|
||||||
|
@ -1,24 +1,18 @@
|
|||||||
"""User."""
|
"""User."""
|
||||||
import jwt
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
from urllib.parse import urlencode, quote
|
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from flask import g
|
import jwt
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
from flask import g
|
||||||
from flask import redirect
|
from flask import redirect
|
||||||
from flask import request
|
|
||||||
from flask.app import Flask
|
|
||||||
from flask_bpmn.api.api_error import ApiError
|
from flask_bpmn.api.api_error import ApiError
|
||||||
from flask_bpmn.models.db import db
|
|
||||||
|
|
||||||
|
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.authentication_service import PublicAuthenticationService, get_keycloak_args
|
from spiffworkflow_backend.services.authentication_service import (
|
||||||
|
PublicAuthenticationService,
|
||||||
|
)
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||||
from spiffworkflow_backend.services.user_service import UserService
|
from spiffworkflow_backend.services.user_service import UserService
|
||||||
|
|
||||||
@ -27,6 +21,7 @@ from spiffworkflow_backend.services.user_service import UserService
|
|||||||
:synopsis: Single Sign On (SSO) user login and session handlers
|
:synopsis: Single Sign On (SSO) user login and session handlers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def verify_token(token: Optional[str] = None) -> Dict[str, Optional[str]]:
|
def verify_token(token: Optional[str] = None) -> Dict[str, Optional[str]]:
|
||||||
"""Verify the token for the user (if provided).
|
"""Verify the token for the user (if provided).
|
||||||
|
|
||||||
@ -42,34 +37,37 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[str]]:
|
|||||||
ApiError: If not on production and token is not valid, returns an 'invalid_token' 403 error.
|
ApiError: If not on production and token is not valid, returns an 'invalid_token' 403 error.
|
||||||
If on production and user is not authenticated, returns a 'no_user' 403 error.
|
If on production and user is not authenticated, returns a 'no_user' 403 error.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if token:
|
if token:
|
||||||
user_info = None
|
user_info = None
|
||||||
|
|
||||||
token_type = get_token_type(token)
|
token_type = get_token_type(token)
|
||||||
if token_type == 'id_token' :
|
if token_type == "id_token":
|
||||||
try:
|
try:
|
||||||
user_info = AuthorizationService().get_user_info_from_id_token(token)
|
user_info = AuthorizationService().get_user_info_from_id_token(token)
|
||||||
except ApiError as ae:
|
except ApiError as ae:
|
||||||
raise ae
|
raise ae
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"Exception raised in get_token: {e}")
|
current_app.logger.error(f"Exception raised in get_token: {e}")
|
||||||
raise ApiError(code="fail_get_user_info",
|
raise ApiError(
|
||||||
message="Cannot get user info from token")
|
code="fail_get_user_info", message="Cannot get user info from token"
|
||||||
|
)
|
||||||
|
|
||||||
if user_info and 'error' not in user_info: # not sure what to test yet
|
if user_info and "error" not in user_info: # not sure what to test yet
|
||||||
user_model = UserModel.query\
|
user_model = (
|
||||||
.filter(UserModel.service == 'keycloak')\
|
UserModel.query.filter(UserModel.service == "keycloak")
|
||||||
.filter(UserModel.service_id==user_info['sub'])\
|
.filter(UserModel.service_id == user_info["sub"])
|
||||||
.first()
|
.first()
|
||||||
|
)
|
||||||
if user_model is None:
|
if user_model is None:
|
||||||
# Do we ever get here any more, now that we have login_return method?
|
# Do we ever get here any more, now that we have login_return method?
|
||||||
current_app.logger.debug("create_user in verify_token")
|
current_app.logger.debug("create_user in verify_token")
|
||||||
user_model = UserService().create_user(service='keycloak',
|
user_model = UserService().create_user(
|
||||||
service_id=user_info['sub'],
|
service="keycloak",
|
||||||
name=user_info['name'],
|
service_id=user_info["sub"],
|
||||||
username=user_info['preferred_username'],
|
name=user_info["name"],
|
||||||
email=user_info['email'])
|
username=user_info["preferred_username"],
|
||||||
|
email=user_info["email"],
|
||||||
|
)
|
||||||
if user_model:
|
if user_model:
|
||||||
g.user = user_model
|
g.user = user_model
|
||||||
|
|
||||||
@ -77,22 +75,18 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[str]]:
|
|||||||
if g.user:
|
if g.user:
|
||||||
g.token = token
|
g.token = token
|
||||||
scope = get_scope(token)
|
scope = get_scope(token)
|
||||||
return {'uid': g.user.id,
|
return {"uid": g.user.id, "sub": g.user.id, "scope": scope}
|
||||||
'sub': g.user.id,
|
|
||||||
'scope': scope}
|
|
||||||
# return validate_scope(token, user_info, user_model)
|
# return validate_scope(token, user_info, user_model)
|
||||||
else:
|
else:
|
||||||
raise ApiError(code="no_user_id",
|
raise ApiError(code="no_user_id", message="Cannot get a user id")
|
||||||
message="Cannot get a user id")
|
|
||||||
|
|
||||||
# no user_info
|
# no user_info
|
||||||
else:
|
else:
|
||||||
raise ApiError(code="no_user_info",
|
raise ApiError(code="no_user_info", message="Cannot retrieve user info")
|
||||||
message="Cannot retrieve user info")
|
|
||||||
|
|
||||||
# no token -- do we ever get here?
|
# no token -- do we ever get here?
|
||||||
else:
|
else:
|
||||||
if app.config.get('DEVELOPMENT'):
|
if app.config.get("DEVELOPMENT"):
|
||||||
# Fall back to a default user if this is not production.
|
# Fall back to a default user if this is not production.
|
||||||
g.user = UserModel.query.first()
|
g.user = UserModel.query.first()
|
||||||
if not g.user:
|
if not g.user:
|
||||||
@ -105,11 +99,15 @@ def verify_token(token: Optional[str] = None) -> Dict[str, Optional[str]]:
|
|||||||
return token_info
|
return token_info
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ApiError(code="no_auth_token",
|
raise ApiError(
|
||||||
|
code="no_auth_token",
|
||||||
message="No authorization token was available.",
|
message="No authorization token was available.",
|
||||||
status_code=401)
|
status_code=401,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_scope(token) -> bool:
|
def validate_scope(token) -> bool:
|
||||||
|
"""Validate_scope."""
|
||||||
print("validate_scope")
|
print("validate_scope")
|
||||||
# token = AuthorizationService().refresh_token(token)
|
# token = AuthorizationService().refresh_token(token)
|
||||||
# user_info = AuthorizationService().get_user_info_from_public_access_token(token)
|
# user_info = AuthorizationService().get_user_info_from_public_access_token(token)
|
||||||
@ -119,80 +117,101 @@ def validate_scope(token) -> bool:
|
|||||||
# introspection = AuthorizationService().introspect_token(basic_token)
|
# introspection = AuthorizationService().introspect_token(basic_token)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def api_login(uid, password, redirect_url=None):
|
|
||||||
|
|
||||||
|
def api_login(uid, password, redirect_url=None):
|
||||||
|
"""Api_login."""
|
||||||
token = PublicAuthenticationService().get_public_access_token(uid, password)
|
token = PublicAuthenticationService().get_public_access_token(uid, password)
|
||||||
g.token = token
|
g.token = token
|
||||||
|
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
||||||
def encode_auth_token(uid):
|
def encode_auth_token(uid):
|
||||||
"""
|
"""
|
||||||
Generates the Auth Token
|
Generates the Auth Token
|
||||||
:return: string
|
:return: string
|
||||||
"""
|
"""
|
||||||
payload = {
|
payload = {"sub": uid}
|
||||||
'sub': uid
|
|
||||||
}
|
|
||||||
return jwt.encode(
|
return jwt.encode(
|
||||||
payload,
|
payload,
|
||||||
app.config.get('SECRET_KEY'),
|
app.config.get("SECRET_KEY"),
|
||||||
algorithm='HS256',
|
algorithm="HS256",
|
||||||
)
|
)
|
||||||
|
|
||||||
def login(redirect_url='/'):
|
|
||||||
|
def login(redirect_url="/"):
|
||||||
|
"""Login."""
|
||||||
state = PublicAuthenticationService.generate_state(redirect_url)
|
state = PublicAuthenticationService.generate_state(redirect_url)
|
||||||
login_redirect_url = PublicAuthenticationService().get_login_redirect_url(state)
|
login_redirect_url = PublicAuthenticationService().get_login_redirect_url(state)
|
||||||
return redirect(login_redirect_url)
|
return redirect(login_redirect_url)
|
||||||
|
|
||||||
|
|
||||||
def login_return(code, state, session_state):
|
def login_return(code, state, session_state):
|
||||||
""""""
|
""""""
|
||||||
# TODO: Why does state look like this?
|
# TODO: Why does state look like this?
|
||||||
# 'b\'eydyZWRpcmVjdF91cmwnOiAnaHR0cDovL2xvY2FsaG9zdDo3MDAxLyd9\''
|
# 'b\'eydyZWRpcmVjdF91cmwnOiAnaHR0cDovL2xvY2FsaG9zdDo3MDAxLyd9\''
|
||||||
# It has an extra 'b at the beginning and an extra ' at the end,
|
# It has an extra 'b at the beginning and an extra ' at the end,
|
||||||
# so we use state[2:-1]
|
# so we use state[2:-1]
|
||||||
state_dict = eval(base64.b64decode(state[2:-1]).decode('utf-8'))
|
state_dict = eval(base64.b64decode(state[2:-1]).decode("utf-8"))
|
||||||
state_redirect_url = state_dict['redirect_url']
|
state_redirect_url = state_dict["redirect_url"]
|
||||||
|
|
||||||
id_token_object = PublicAuthenticationService().get_id_token_object(code)
|
id_token_object = PublicAuthenticationService().get_id_token_object(code)
|
||||||
id_token = id_token_object['id_token']
|
id_token = id_token_object["id_token"]
|
||||||
|
|
||||||
if PublicAuthenticationService.validate_id_token(id_token):
|
if PublicAuthenticationService.validate_id_token(id_token):
|
||||||
user_info = AuthorizationService().get_user_info_from_id_token(id_token_object['access_token'])
|
user_info = AuthorizationService().get_user_info_from_id_token(
|
||||||
if user_info and 'error' not in user_info:
|
id_token_object["access_token"]
|
||||||
user_model = UserModel.query.filter(UserModel.service == 'keycloak').filter(UserModel.service_id==user_info['sub']).first()
|
)
|
||||||
|
if user_info and "error" not in user_info:
|
||||||
|
user_model = (
|
||||||
|
UserModel.query.filter(UserModel.service == "keycloak")
|
||||||
|
.filter(UserModel.service_id == user_info["sub"])
|
||||||
|
.first()
|
||||||
|
)
|
||||||
if user_model is None:
|
if user_model is None:
|
||||||
current_app.logger.debug("create_user in login_return")
|
current_app.logger.debug("create_user in login_return")
|
||||||
name = username = email = ''
|
name = username = email = ""
|
||||||
if 'name' in user_info:
|
if "name" in user_info:
|
||||||
name = user_info['name']
|
name = user_info["name"]
|
||||||
if 'username' in user_info:
|
if "username" in user_info:
|
||||||
username = user_info['username']
|
username = user_info["username"]
|
||||||
if 'email' in user_info:
|
if "email" in user_info:
|
||||||
email = user_info['email']
|
email = user_info["email"]
|
||||||
user_model = UserService().create_user(service='keycloak',
|
user_model = UserService().create_user(
|
||||||
service_id=user_info['sub'],
|
service="keycloak",
|
||||||
|
service_id=user_info["sub"],
|
||||||
name=name,
|
name=name,
|
||||||
username=username,
|
username=username,
|
||||||
email=email)
|
email=email,
|
||||||
|
)
|
||||||
|
|
||||||
if user_model:
|
if user_model:
|
||||||
g.user = user_model.id
|
g.user = user_model.id
|
||||||
|
|
||||||
redirect_url = f"{state_redirect_url}?" + \
|
redirect_url = (
|
||||||
f"access_token={id_token_object['access_token']}&" + \
|
f"{state_redirect_url}?"
|
||||||
f"id_token={id_token}"
|
+ f"access_token={id_token_object['access_token']}&"
|
||||||
|
+ f"id_token={id_token}"
|
||||||
|
)
|
||||||
return redirect(redirect_url)
|
return redirect(redirect_url)
|
||||||
|
|
||||||
# return f"{code} {state} {id_token}"
|
# return f"{code} {state} {id_token}"
|
||||||
|
|
||||||
|
|
||||||
def logout(id_token: str, redirect_url: str | None):
|
def logout(id_token: str, redirect_url: str | None):
|
||||||
return PublicAuthenticationService().logout(id_token=id_token, redirect_url=redirect_url)
|
"""Logout."""
|
||||||
|
return PublicAuthenticationService().logout(
|
||||||
|
id_token=id_token, redirect_url=redirect_url
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def logout_return():
|
def logout_return():
|
||||||
|
"""Logout_return."""
|
||||||
return redirect(f"http://localhost:7001/")
|
return redirect(f"http://localhost:7001/")
|
||||||
|
|
||||||
|
|
||||||
def get_token_type(token) -> bool:
|
def get_token_type(token) -> bool:
|
||||||
|
"""Get_token_type."""
|
||||||
token_type = None
|
token_type = None
|
||||||
try:
|
try:
|
||||||
PublicAuthenticationService.validate_id_token(token)
|
PublicAuthenticationService.validate_id_token(token)
|
||||||
@ -203,7 +222,7 @@ def get_token_type(token) -> bool:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Exception in get_token_type: {e}")
|
print(f"Exception in get_token_type: {e}")
|
||||||
else:
|
else:
|
||||||
token_type = 'id_token'
|
token_type = "id_token"
|
||||||
# try:
|
# try:
|
||||||
# # see if we have an open_id token
|
# # see if we have an open_id token
|
||||||
# decoded_token = AuthorizationService.decode_auth_token(token)
|
# decoded_token = AuthorizationService.decode_auth_token(token)
|
||||||
@ -215,7 +234,9 @@ def get_token_type(token) -> bool:
|
|||||||
# return True
|
# return True
|
||||||
return token_type
|
return token_type
|
||||||
|
|
||||||
|
|
||||||
def get_scope(token):
|
def get_scope(token):
|
||||||
|
"""Get_scope."""
|
||||||
decoded_token = jwt.decode(token, options={"verify_signature": False})
|
decoded_token = jwt.decode(token, options={"verify_signature": False})
|
||||||
scope = decoded_token['scope']
|
scope = decoded_token["scope"]
|
||||||
return scope
|
return scope
|
||||||
|
@ -5,7 +5,6 @@ from typing import Final
|
|||||||
|
|
||||||
import flask.wrappers
|
import flask.wrappers
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import current_app
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import Response
|
from flask import Response
|
||||||
from flask_bpmn.api.api_error import ApiError
|
from flask_bpmn.api.api_error import ApiError
|
||||||
@ -13,7 +12,6 @@ from flask_bpmn.models.db import db
|
|||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from spiffworkflow_backend.models.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
from spiffworkflow_backend.models.principal import PrincipalModel
|
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel
|
from spiffworkflow_backend.models.user_group_assignment import UserGroupAssignmentModel
|
||||||
|
|
||||||
|
@ -1,37 +1,42 @@
|
|||||||
"""Authentication_service."""
|
"""Authentication_service."""
|
||||||
|
import base64
|
||||||
|
import enum
|
||||||
|
import json
|
||||||
|
import time
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import requests
|
|
||||||
import base64
|
|
||||||
import json
|
|
||||||
import enum
|
|
||||||
import random
|
|
||||||
import jwt
|
import jwt
|
||||||
import time
|
import requests
|
||||||
from flask import g
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import redirect
|
from flask import redirect
|
||||||
from flask_bpmn.api.api_error import ApiError
|
from flask_bpmn.api.api_error import ApiError
|
||||||
from flask_bpmn.models.db import db
|
|
||||||
|
|
||||||
from keycloak import KeycloakOpenID # type: ignore
|
from keycloak import KeycloakOpenID # type: ignore
|
||||||
from keycloak.uma_permissions import AuthStatus # type: ignore
|
from keycloak.uma_permissions import AuthStatus # type: ignore
|
||||||
from keycloak import KeycloakAdmin
|
|
||||||
|
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||||
|
|
||||||
|
|
||||||
def get_keycloak_args():
|
def get_keycloak_args():
|
||||||
keycloak_server_url = current_app.config['KEYCLOAK_SERVER_URL']
|
"""Get_keycloak_args."""
|
||||||
|
keycloak_server_url = current_app.config["KEYCLOAK_SERVER_URL"]
|
||||||
keycloak_client_id = current_app.config["KEYCLOAK_CLIENT_ID"]
|
keycloak_client_id = current_app.config["KEYCLOAK_CLIENT_ID"]
|
||||||
keycloak_realm_name = current_app.config["KEYCLOAK_REALM_NAME"]
|
keycloak_realm_name = current_app.config["KEYCLOAK_REALM_NAME"]
|
||||||
keycloak_client_secret_key = current_app.config["KEYCLOAK_CLIENT_SECRET_KEY"] # noqa: S105
|
keycloak_client_secret_key = current_app.config[
|
||||||
return keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key
|
"KEYCLOAK_CLIENT_SECRET_KEY"
|
||||||
|
] # noqa: S105
|
||||||
|
return (
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationServiceProviders(enum.Enum):
|
class AuthenticationServiceProviders(enum.Enum):
|
||||||
keycloak = 'keycloak'
|
"""AuthenticationServiceProviders."""
|
||||||
internal = 'internal'
|
|
||||||
|
keycloak = "keycloak"
|
||||||
|
internal = "internal"
|
||||||
|
|
||||||
|
|
||||||
class PublicAuthenticationService:
|
class PublicAuthenticationService:
|
||||||
@ -39,46 +44,72 @@ class PublicAuthenticationService:
|
|||||||
It uses a separate public keycloak client: spiffworkflow-frontend
|
It uses a separate public keycloak client: spiffworkflow-frontend
|
||||||
Used during development to make testing easy.
|
Used during development to make testing easy.
|
||||||
"""
|
"""
|
||||||
def logout(self, redirect_url: str='/', id_token: str | None=None):
|
|
||||||
if id_token is None:
|
|
||||||
raise ApiError(code='missing_id_token',
|
|
||||||
message="id_token is missing",
|
|
||||||
status_code=400)
|
|
||||||
|
|
||||||
return_redirect_url = 'http://localhost:7000/v1.0/logout_return'
|
def logout(self, redirect_url: str = "/", id_token: str | None = None):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = get_keycloak_args()
|
"""Logout."""
|
||||||
|
if id_token is None:
|
||||||
|
raise ApiError(
|
||||||
|
code="missing_id_token", message="id_token is missing", status_code=400
|
||||||
|
)
|
||||||
|
|
||||||
|
return_redirect_url = "http://localhost:7000/v1.0/logout_return"
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = get_keycloak_args()
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/logout?post_logout_redirect_uri={return_redirect_url}&id_token_hint={id_token}"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/logout?post_logout_redirect_uri={return_redirect_url}&id_token_hint={id_token}"
|
||||||
|
|
||||||
return redirect(request_url)
|
return redirect(request_url)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_state(redirect_url):
|
def generate_state(redirect_url):
|
||||||
state = base64.b64encode(bytes(str({'redirect_url': redirect_url}), 'UTF-8'))
|
"""Generate_state."""
|
||||||
|
state = base64.b64encode(bytes(str({"redirect_url": redirect_url}), "UTF-8"))
|
||||||
return state
|
return state
|
||||||
|
|
||||||
def get_login_redirect_url(self, state):
|
def get_login_redirect_url(self, state):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = get_keycloak_args()
|
"""Get_login_redirect_url."""
|
||||||
return_redirect_url = 'http://localhost:7000/v1.0/login_return'
|
(
|
||||||
login_redirect_url = f'{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/auth?' + \
|
keycloak_server_url,
|
||||||
f'state={state}&' + \
|
keycloak_client_id,
|
||||||
'response_type=code&' + \
|
keycloak_realm_name,
|
||||||
f'client_id={keycloak_client_id}&' + \
|
keycloak_client_secret_key,
|
||||||
'scope=openid&' + \
|
) = get_keycloak_args()
|
||||||
f'redirect_uri={return_redirect_url}'
|
return_redirect_url = "http://localhost:7000/v1.0/login_return"
|
||||||
|
login_redirect_url = (
|
||||||
|
f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/auth?"
|
||||||
|
+ f"state={state}&"
|
||||||
|
+ "response_type=code&"
|
||||||
|
+ f"client_id={keycloak_client_id}&"
|
||||||
|
+ "scope=openid&"
|
||||||
|
+ f"redirect_uri={return_redirect_url}"
|
||||||
|
)
|
||||||
return login_redirect_url
|
return login_redirect_url
|
||||||
|
|
||||||
def get_id_token_object(self, code):
|
def get_id_token_object(self, code):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = get_keycloak_args()
|
"""Get_id_token_object."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = get_keycloak_args()
|
||||||
|
|
||||||
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
||||||
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding='ascii')
|
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding="ascii")
|
||||||
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}"}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}",
|
||||||
|
}
|
||||||
|
|
||||||
data = {'grant_type': 'authorization_code',
|
data = {
|
||||||
'code': code,
|
"grant_type": "authorization_code",
|
||||||
'redirect_uri': 'http://localhost:7000/v1.0/login_return'}
|
"code": code,
|
||||||
|
"redirect_uri": "http://localhost:7000/v1.0/login_return",
|
||||||
|
}
|
||||||
|
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
|
|
||||||
@ -92,58 +123,80 @@ class PublicAuthenticationService:
|
|||||||
https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||||
"""
|
"""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = get_keycloak_args()
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = get_keycloak_args()
|
||||||
try:
|
try:
|
||||||
decoded_token = jwt.decode(id_token, options={"verify_signature": False})
|
decoded_token = jwt.decode(id_token, options={"verify_signature": False})
|
||||||
except Exception as e:
|
except Exception:
|
||||||
raise ApiError(code='bad_id_token',
|
raise ApiError(
|
||||||
message="Cannot decode id_token",
|
code="bad_id_token", message="Cannot decode id_token", status_code=401
|
||||||
status_code=401)
|
)
|
||||||
try:
|
try:
|
||||||
assert decoded_token['iss'] == f"{keycloak_server_url}/realms/{keycloak_realm_name}"
|
assert (
|
||||||
assert keycloak_client_id in decoded_token['aud'] or 'account' in decoded_token['aud']
|
decoded_token["iss"]
|
||||||
if 'azp' in decoded_token:
|
== f"{keycloak_server_url}/realms/{keycloak_realm_name}"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
keycloak_client_id in decoded_token["aud"]
|
||||||
|
or "account" in decoded_token["aud"]
|
||||||
|
)
|
||||||
|
if "azp" in decoded_token:
|
||||||
# TODO: not sure why this isn't keycloak_client_id
|
# TODO: not sure why this isn't keycloak_client_id
|
||||||
assert decoded_token['azp'] in keycloak_client_id, 'account'
|
assert decoded_token["azp"] in keycloak_client_id, "account"
|
||||||
assert now > decoded_token['iat']
|
assert now > decoded_token["iat"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"Exception validating id_token: {e}")
|
current_app.logger.error(f"Exception validating id_token: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
assert now < decoded_token['exp']
|
assert now < decoded_token["exp"]
|
||||||
except:
|
except:
|
||||||
raise ApiError(code='invalid_token',
|
raise ApiError(
|
||||||
|
code="invalid_token",
|
||||||
message="Your token is expired. Please Login",
|
message="Your token is expired. Please Login",
|
||||||
status_code=401)
|
status_code=401,
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_public_access_token(self, username, password) -> dict:
|
def get_public_access_token(self, username, password) -> dict:
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
"""Get_public_access_token."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
# Get public access token
|
# Get public access token
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||||
post_data = {'grant_type': 'password',
|
post_data = {
|
||||||
'username': username,
|
"grant_type": "password",
|
||||||
'password': password,
|
"username": username,
|
||||||
'client_id': 'spiffworkflow-frontend'
|
"password": password,
|
||||||
|
"client_id": "spiffworkflow-frontend",
|
||||||
}
|
}
|
||||||
public_response = requests.post(request_url, headers=headers, data=post_data)
|
public_response = requests.post(request_url, headers=headers, data=post_data)
|
||||||
if public_response.status_code == 200:
|
if public_response.status_code == 200:
|
||||||
public_token = json.loads(public_response.text)
|
public_token = json.loads(public_response.text)
|
||||||
if 'access_token' in public_token:
|
if "access_token" in public_token:
|
||||||
return public_token['access_token']
|
return public_token["access_token"]
|
||||||
raise ApiError(code='no_public_access_token',
|
raise ApiError(
|
||||||
message=f"We could not get a public access token: {username}")
|
code="no_public_access_token",
|
||||||
|
message=f"We could not get a public access token: {username}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationService:
|
class AuthenticationService:
|
||||||
"""AuthenticationService."""
|
"""AuthenticationService."""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class KeycloakAuthenticationService:
|
class KeycloakAuthenticationService:
|
||||||
|
"""KeycloakAuthenticationService."""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_keycloak_openid(
|
def get_keycloak_openid(
|
||||||
@ -157,6 +210,7 @@ class KeycloakAuthenticationService:
|
|||||||
client_secret_key=client_secret_key,
|
client_secret_key=client_secret_key,
|
||||||
)
|
)
|
||||||
return keycloak_openid
|
return keycloak_openid
|
||||||
|
|
||||||
#
|
#
|
||||||
# @staticmethod
|
# @staticmethod
|
||||||
# def get_keycloak_token(keycloak_openid, user, password):
|
# def get_keycloak_token(keycloak_openid, user, password):
|
||||||
@ -188,6 +242,7 @@ class KeycloakAuthenticationService:
|
|||||||
# permissions: list = keycloak_openid.uma_permissions(token["access_token"])
|
# permissions: list = keycloak_openid.uma_permissions(token["access_token"])
|
||||||
# return permissions
|
# return permissions
|
||||||
#
|
#
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_uma_permissions_by_token_for_resource_and_scope(
|
def get_uma_permissions_by_token_for_resource_and_scope(
|
||||||
keycloak_openid: KeycloakOpenID, token: dict, resource: str, scope: str
|
keycloak_openid: KeycloakOpenID, token: dict, resource: str, scope: str
|
||||||
@ -197,6 +252,8 @@ class KeycloakAuthenticationService:
|
|||||||
token["access_token"], permissions=f"{resource}#{scope}"
|
token["access_token"], permissions=f"{resource}#{scope}"
|
||||||
)
|
)
|
||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# @staticmethod
|
# @staticmethod
|
||||||
# def get_auth_status_for_resource_and_scope_by_token(
|
# def get_auth_status_for_resource_and_scope_by_token(
|
||||||
@ -228,5 +285,6 @@ class KeycloakAuthenticationService:
|
|||||||
|
|
||||||
|
|
||||||
class KeyCloak:
|
class KeyCloak:
|
||||||
|
"""KeyCloak."""
|
||||||
|
|
||||||
"""Class to interact with KeyCloak server for authorization"""
|
"""Class to interact with KeyCloak server for authorization."""
|
||||||
|
@ -1,35 +1,46 @@
|
|||||||
"""Authorization_service."""
|
"""Authorization_service."""
|
||||||
import requests
|
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
import jwt
|
|
||||||
import enum
|
|
||||||
from flask import g
|
|
||||||
from flask import current_app
|
|
||||||
from flask_bpmn.api.api_error import ApiError
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
import jwt
|
||||||
|
import requests
|
||||||
|
from flask import current_app
|
||||||
|
from flask_bpmn.api.api_error import ApiError
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationService:
|
class AuthorizationService:
|
||||||
"""Determine whether a user has permission to perform their request."""
|
"""Determine whether a user has permission to perform their request."""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_keycloak_args():
|
def get_keycloak_args():
|
||||||
keycloak_server_url = current_app.config['KEYCLOAK_SERVER_URL']
|
"""Get_keycloak_args."""
|
||||||
|
keycloak_server_url = current_app.config["KEYCLOAK_SERVER_URL"]
|
||||||
keycloak_client_id = current_app.config["KEYCLOAK_CLIENT_ID"]
|
keycloak_client_id = current_app.config["KEYCLOAK_CLIENT_ID"]
|
||||||
keycloak_realm_name = current_app.config["KEYCLOAK_REALM_NAME"]
|
keycloak_realm_name = current_app.config["KEYCLOAK_REALM_NAME"]
|
||||||
keycloak_client_secret_key = current_app.config["KEYCLOAK_CLIENT_SECRET_KEY"] # noqa: S105
|
keycloak_client_secret_key = current_app.config[
|
||||||
return keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key
|
"KEYCLOAK_CLIENT_SECRET_KEY"
|
||||||
|
] # noqa: S105
|
||||||
|
return (
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
)
|
||||||
|
|
||||||
def get_user_info_from_id_token(self, token):
|
def get_user_info_from_id_token(self, token):
|
||||||
"""This seems to work with basic tokens too"""
|
"""This seems to work with basic tokens too."""
|
||||||
json_data = None
|
json_data = None
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
|
|
||||||
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
||||||
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding='ascii')
|
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding="ascii")
|
||||||
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
||||||
|
|
||||||
# headers = {"Content-Type": "application/x-www-form-urlencoded",
|
# headers = {"Content-Type": "application/x-www-form-urlencoded",
|
||||||
# "Authorization": f"Bearer {BACKEND_BASIC_AUTH.decode('utf-8')}"}
|
# "Authorization": f"Bearer {BACKEND_BASIC_AUTH.decode('utf-8')}"}
|
||||||
@ -46,11 +57,13 @@ class AuthorizationService:
|
|||||||
# auth_bearer_string = f"Basic {keycloak_client_secret_key}"
|
# auth_bearer_string = f"Basic {keycloak_client_secret_key}"
|
||||||
|
|
||||||
headers = {"Authorization": f"Bearer {token}"}
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
|
data = {
|
||||||
'client_id': keycloak_client_id,
|
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
||||||
|
"client_id": keycloak_client_id,
|
||||||
# "subject_token": id_token_object['access_token'],
|
# "subject_token": id_token_object['access_token'],
|
||||||
"subject_token": token,
|
"subject_token": token,
|
||||||
"audience": keycloak_client_id}
|
"audience": keycloak_client_id,
|
||||||
|
}
|
||||||
|
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/userinfo"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/userinfo"
|
||||||
try:
|
try:
|
||||||
@ -60,38 +73,56 @@ class AuthorizationService:
|
|||||||
if request_response.status_code == 200:
|
if request_response.status_code == 200:
|
||||||
json_data = json.loads(request_response.text)
|
json_data = json.loads(request_response.text)
|
||||||
elif request_response.status_code == 401:
|
elif request_response.status_code == 401:
|
||||||
raise ApiError(code="invalid_token",
|
raise ApiError(
|
||||||
message="Please login",
|
code="invalid_token", message="Please login", status_code=401
|
||||||
status_code=401)
|
)
|
||||||
return json_data
|
return json_data
|
||||||
|
|
||||||
def refresh_token(self, token):
|
def refresh_token(self, token):
|
||||||
|
"""Refresh_token."""
|
||||||
# if isinstance(token, str):
|
# if isinstance(token, str):
|
||||||
# token = eval(token)
|
# token = eval(token)
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
data = {'grant_type': 'refresh_token',
|
data = {
|
||||||
'client_id': 'spiffworkflow-frontend',
|
"grant_type": "refresh_token",
|
||||||
'subject_token': token,
|
"client_id": "spiffworkflow-frontend",
|
||||||
'refresh_token': token}
|
"subject_token": token,
|
||||||
|
"refresh_token": token,
|
||||||
|
}
|
||||||
refresh_response = requests.post(request_url, headers=headers, data=data)
|
refresh_response = requests.post(request_url, headers=headers, data=data)
|
||||||
refresh_token = json.loads(refresh_response.text)
|
refresh_token = json.loads(refresh_response.text)
|
||||||
return refresh_token
|
return refresh_token
|
||||||
|
|
||||||
def get_bearer_token(self, basic_token):
|
def get_bearer_token(self, basic_token):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
"""Get_bearer_token."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
|
|
||||||
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
||||||
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding='ascii')
|
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding="ascii")
|
||||||
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
||||||
|
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}"}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
|
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}",
|
||||||
'client_id': keycloak_client_id,
|
}
|
||||||
|
data = {
|
||||||
|
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
||||||
|
"client_id": keycloak_client_id,
|
||||||
"subject_token": basic_token,
|
"subject_token": basic_token,
|
||||||
"audience": keycloak_client_id}
|
"audience": keycloak_client_id,
|
||||||
|
}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
|
|
||||||
backend_response = requests.post(request_url, headers=headers, data=data)
|
backend_response = requests.post(request_url, headers=headers, data=data)
|
||||||
@ -126,20 +157,31 @@ class AuthorizationService:
|
|||||||
) from exception
|
) from exception
|
||||||
|
|
||||||
def get_bearer_token_from_internal_token(self, internal_token):
|
def get_bearer_token_from_internal_token(self, internal_token):
|
||||||
decoded_token = self.decode_auth_token(internal_token)
|
"""Get_bearer_token_from_internal_token."""
|
||||||
|
self.decode_auth_token(internal_token)
|
||||||
print(f"get_user_by_internal_token: {internal_token}")
|
print(f"get_user_by_internal_token: {internal_token}")
|
||||||
|
|
||||||
def introspect_token(self, basic_token):
|
def introspect_token(self, basic_token):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
"""Introspect_token."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
|
|
||||||
bearer_token = AuthorizationService().get_bearer_token(basic_token)
|
bearer_token = AuthorizationService().get_bearer_token(basic_token)
|
||||||
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
||||||
|
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
data = {'client_id': keycloak_client_id,
|
"Authorization": auth_bearer_string,
|
||||||
'client_secret': keycloak_client_secret_key,
|
}
|
||||||
'token': basic_token}
|
data = {
|
||||||
|
"client_id": keycloak_client_id,
|
||||||
|
"client_secret": keycloak_client_secret_key,
|
||||||
|
"token": basic_token,
|
||||||
|
}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token/introspect"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token/introspect"
|
||||||
|
|
||||||
introspect_response = requests.post(request_url, headers=headers, data=data)
|
introspect_response = requests.post(request_url, headers=headers, data=data)
|
||||||
@ -148,7 +190,13 @@ class AuthorizationService:
|
|||||||
return introspection
|
return introspection
|
||||||
|
|
||||||
def get_permission_by_basic_token(self, basic_token):
|
def get_permission_by_basic_token(self, basic_token):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
"""Get_permission_by_basic_token."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
|
|
||||||
# basic_token = AuthorizationService().refresh_token(basic_token)
|
# basic_token = AuthorizationService().refresh_token(basic_token)
|
||||||
# bearer_token = AuthorizationService().get_bearer_token(basic_token['access_token'])
|
# bearer_token = AuthorizationService().get_bearer_token(basic_token['access_token'])
|
||||||
@ -156,36 +204,49 @@ class AuthorizationService:
|
|||||||
# auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
# auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
||||||
auth_bearer_string = f"Bearer {bearer_token}"
|
auth_bearer_string = f"Bearer {bearer_token}"
|
||||||
|
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
data = {'client_id': keycloak_client_id,
|
"Authorization": auth_bearer_string,
|
||||||
'client_secret': keycloak_client_secret_key,
|
}
|
||||||
"grant_type": 'urn:ietf:params:oauth:grant-type:uma-ticket',
|
data = {
|
||||||
|
"client_id": keycloak_client_id,
|
||||||
|
"client_secret": keycloak_client_secret_key,
|
||||||
|
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
|
||||||
"response_mode": "permissions",
|
"response_mode": "permissions",
|
||||||
"audience": keycloak_client_id,
|
"audience": keycloak_client_id,
|
||||||
"response_include_resource_name": True
|
"response_include_resource_name": True,
|
||||||
}
|
}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
permission_response = requests.post(request_url, headers=headers, data=data)
|
permission_response = requests.post(request_url, headers=headers, data=data)
|
||||||
permission = json.loads(permission_response.text)
|
permission = json.loads(permission_response.text)
|
||||||
return permission
|
return permission
|
||||||
|
|
||||||
def get_auth_status_for_resource_and_scope_by_token(self, basic_token, resource, scope):
|
def get_auth_status_for_resource_and_scope_by_token(
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
self, basic_token, resource, scope
|
||||||
|
):
|
||||||
|
"""Get_auth_status_for_resource_and_scope_by_token."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
|
|
||||||
# basic_token = AuthorizationService().refresh_token(basic_token)
|
# basic_token = AuthorizationService().refresh_token(basic_token)
|
||||||
bearer_token = AuthorizationService().get_bearer_token(basic_token)
|
bearer_token = AuthorizationService().get_bearer_token(basic_token)
|
||||||
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
||||||
|
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"Authorization": auth_bearer_string,
|
||||||
|
}
|
||||||
data = {
|
data = {
|
||||||
'client_id': keycloak_client_id,
|
"client_id": keycloak_client_id,
|
||||||
'client_secret': keycloak_client_secret_key,
|
"client_secret": keycloak_client_secret_key,
|
||||||
"grant_type": 'urn:ietf:params:oauth:grant-type:uma-ticket',
|
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
|
||||||
"permission": f"{resource}#{scope}",
|
"permission": f"{resource}#{scope}",
|
||||||
"response_mode": "permissions",
|
"response_mode": "permissions",
|
||||||
"audience": keycloak_client_id
|
"audience": keycloak_client_id,
|
||||||
}
|
}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
auth_response = requests.post(request_url, headers=headers, data=data)
|
auth_response = requests.post(request_url, headers=headers, data=data)
|
||||||
@ -194,28 +255,39 @@ class AuthorizationService:
|
|||||||
auth_status = json.loads(auth_response.text)
|
auth_status = json.loads(auth_response.text)
|
||||||
return auth_status
|
return auth_status
|
||||||
|
|
||||||
def get_permissions_by_token_for_resource_and_scope(self, basic_token, resource=None, scope=None):
|
def get_permissions_by_token_for_resource_and_scope(
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
self, basic_token, resource=None, scope=None
|
||||||
|
):
|
||||||
|
"""Get_permissions_by_token_for_resource_and_scope."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
|
|
||||||
# basic_token = AuthorizationService().refresh_token(basic_token)
|
# basic_token = AuthorizationService().refresh_token(basic_token)
|
||||||
# bearer_token = AuthorizationService().get_bearer_token(basic_token['access_token'])
|
# bearer_token = AuthorizationService().get_bearer_token(basic_token['access_token'])
|
||||||
bearer_token = AuthorizationService().get_bearer_token(basic_token)
|
bearer_token = AuthorizationService().get_bearer_token(basic_token)
|
||||||
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
||||||
|
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
permision = ''
|
"Authorization": auth_bearer_string,
|
||||||
|
}
|
||||||
|
permision = ""
|
||||||
if resource:
|
if resource:
|
||||||
permision += resource
|
permision += resource
|
||||||
if scope:
|
if scope:
|
||||||
permision += '#' + resource
|
permision += "#" + resource
|
||||||
data = {'client_id': keycloak_client_id,
|
data = {
|
||||||
'client_secret': keycloak_client_secret_key,
|
"client_id": keycloak_client_id,
|
||||||
"grant_type": 'urn:ietf:params:oauth:grant-type:uma-ticket',
|
"client_secret": keycloak_client_secret_key,
|
||||||
|
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
|
||||||
"response_mode": "permissions",
|
"response_mode": "permissions",
|
||||||
"permission": permision,
|
"permission": permision,
|
||||||
"audience": keycloak_client_id,
|
"audience": keycloak_client_id,
|
||||||
"response_include_resource_name": True
|
"response_include_resource_name": True,
|
||||||
}
|
}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
permission_response = requests.post(request_url, headers=headers, data=data)
|
permission_response = requests.post(request_url, headers=headers, data=data)
|
||||||
@ -223,16 +295,26 @@ class AuthorizationService:
|
|||||||
return permission
|
return permission
|
||||||
|
|
||||||
def get_resource_set(self, public_access_token, uri):
|
def get_resource_set(self, public_access_token, uri):
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
"""Get_resource_set."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
bearer_token = AuthorizationService().get_bearer_token(public_access_token)
|
bearer_token = AuthorizationService().get_bearer_token(public_access_token)
|
||||||
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
||||||
headers = {"Content-Type": "application/json",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/json",
|
||||||
data = {'matchingUri': 'true',
|
"Authorization": auth_bearer_string,
|
||||||
'deep': 'true',
|
}
|
||||||
'max': '-1',
|
data = {
|
||||||
'exactName': 'false',
|
"matchingUri": "true",
|
||||||
'uri': uri}
|
"deep": "true",
|
||||||
|
"max": "-1",
|
||||||
|
"exactName": "false",
|
||||||
|
"uri": uri,
|
||||||
|
}
|
||||||
|
|
||||||
# f"matchingUri=true&deep=true&max=-1&exactName=false&uri={URI_TO_TEST_AGAINST}"
|
# f"matchingUri=true&deep=true&max=-1&exactName=false&uri={URI_TO_TEST_AGAINST}"
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/authz/protection/resource_set"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/authz/protection/resource_set"
|
||||||
@ -241,13 +323,23 @@ class AuthorizationService:
|
|||||||
print("get_resource_set")
|
print("get_resource_set")
|
||||||
|
|
||||||
def get_permission_by_token(self, public_access_token) -> dict:
|
def get_permission_by_token(self, public_access_token) -> dict:
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = AuthorizationService.get_keycloak_args()
|
"""Get_permission_by_token."""
|
||||||
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = AuthorizationService.get_keycloak_args()
|
||||||
bearer_token = AuthorizationService().get_bearer_token(public_access_token)
|
bearer_token = AuthorizationService().get_bearer_token(public_access_token)
|
||||||
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
auth_bearer_string = f"Bearer {bearer_token['access_token']}"
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
data = {"grant_type": 'urn:ietf:params:oauth:grant-type:uma-ticket',
|
"Authorization": auth_bearer_string,
|
||||||
"audience": keycloak_client_id}
|
}
|
||||||
|
data = {
|
||||||
|
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
|
||||||
|
"audience": keycloak_client_id,
|
||||||
|
}
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
permission_response = requests.post(request_url, headers=headers, data=data)
|
permission_response = requests.post(request_url, headers=headers, data=data)
|
||||||
permission = json.loads(permission_response.text)
|
permission = json.loads(permission_response.text)
|
||||||
|
@ -15,15 +15,18 @@ class UserService:
|
|||||||
"""Provides common tools for working with users."""
|
"""Provides common tools for working with users."""
|
||||||
|
|
||||||
def create_user(self, service, service_id, name=None, username=None, email=None):
|
def create_user(self, service, service_id, name=None, username=None, email=None):
|
||||||
user = UserModel.query.filter(UserModel.service == service)\
|
"""Create_user."""
|
||||||
.filter(UserModel.service_id == service_id)\
|
user = (
|
||||||
|
UserModel.query.filter(UserModel.service == service)
|
||||||
|
.filter(UserModel.service_id == service_id)
|
||||||
.first()
|
.first()
|
||||||
|
)
|
||||||
if name is None:
|
if name is None:
|
||||||
name = ''
|
name = ""
|
||||||
if username is None:
|
if username is None:
|
||||||
username = ''
|
username = ""
|
||||||
if email is None:
|
if email is None:
|
||||||
email = ''
|
email = ""
|
||||||
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
raise (
|
raise (
|
||||||
@ -34,24 +37,29 @@ class UserService:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
user = UserModel(username=username,
|
user = UserModel(
|
||||||
|
username=username,
|
||||||
service=service,
|
service=service,
|
||||||
service_id=service_id,
|
service_id=service_id,
|
||||||
name=name,
|
name=name,
|
||||||
email=email)
|
email=email,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
except IntegrityError as exception:
|
except IntegrityError as exception:
|
||||||
raise (
|
raise (
|
||||||
ApiError(code="integrity_error", message=repr(exception), status_code=500)
|
ApiError(
|
||||||
|
code="integrity_error", message=repr(exception), status_code=500
|
||||||
|
)
|
||||||
) from exception
|
) from exception
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
raise ApiError(code='add_user_error',
|
raise ApiError(
|
||||||
message=f'Could not add user {username}') from e
|
code="add_user_error", message=f"Could not add user {username}"
|
||||||
|
) from e
|
||||||
try:
|
try:
|
||||||
self.create_principal(user.id)
|
self.create_principal(user.id)
|
||||||
except ApiError as ae:
|
except ApiError as ae:
|
||||||
@ -234,6 +242,7 @@ class UserService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def create_principal(self, user_id):
|
def create_principal(self, user_id):
|
||||||
|
"""Create_principal."""
|
||||||
principal = PrincipalModel.query.filter_by(user_id=user_id).first()
|
principal = PrincipalModel.query.filter_by(user_id=user_id).first()
|
||||||
if principal is None:
|
if principal is None:
|
||||||
principal = PrincipalModel(user_id=user_id)
|
principal = PrincipalModel(user_id=user_id)
|
||||||
@ -243,6 +252,8 @@ class UserService:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
current_app.logger.error(f"Exception in create_principal: {e}")
|
current_app.logger.error(f"Exception in create_principal: {e}")
|
||||||
raise ApiError(code="add_principal_error",
|
raise ApiError(
|
||||||
message=f"Could not create principal {user_id}") from e
|
code="add_principal_error",
|
||||||
|
message=f"Could not create principal {user_id}",
|
||||||
|
) from e
|
||||||
return principal
|
return principal
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
"""Base_test."""
|
"""Base_test."""
|
||||||
from flask.app import Flask
|
from flask.app import Flask
|
||||||
from spiffworkflow_backend.services.authentication_service import PublicAuthenticationService
|
|
||||||
|
from spiffworkflow_backend.services.authentication_service import (
|
||||||
|
PublicAuthenticationService,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseTest:
|
class BaseTest:
|
||||||
@ -9,15 +12,24 @@ class BaseTest:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def get_keycloak_constants(app: Flask) -> tuple:
|
def get_keycloak_constants(app: Flask) -> tuple:
|
||||||
"""Get_keycloak_constants."""
|
"""Get_keycloak_constants."""
|
||||||
keycloak_server_url = app.config['KEYCLOAK_SERVER_URL']
|
keycloak_server_url = app.config["KEYCLOAK_SERVER_URL"]
|
||||||
keycloak_client_id = app.config["KEYCLOAK_CLIENT_ID"]
|
keycloak_client_id = app.config["KEYCLOAK_CLIENT_ID"]
|
||||||
keycloak_realm_name = app.config["KEYCLOAK_REALM_NAME"]
|
keycloak_realm_name = app.config["KEYCLOAK_REALM_NAME"]
|
||||||
keycloak_client_secret_key = app.config["KEYCLOAK_CLIENT_SECRET_KEY"] # noqa: S105
|
keycloak_client_secret_key = app.config[
|
||||||
|
"KEYCLOAK_CLIENT_SECRET_KEY"
|
||||||
|
] # noqa: S105
|
||||||
|
|
||||||
return keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key
|
return (
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_public_access_token(username, password) -> dict:
|
def get_public_access_token(username, password) -> dict:
|
||||||
public_access_token = PublicAuthenticationService().get_public_access_token(username, password)
|
"""Get_public_access_token."""
|
||||||
|
public_access_token = PublicAuthenticationService().get_public_access_token(
|
||||||
|
username, password
|
||||||
|
)
|
||||||
return public_access_token
|
return public_access_token
|
||||||
|
|
||||||
|
@ -1,25 +1,17 @@
|
|||||||
"""Test_authentication."""
|
"""Test_authentication."""
|
||||||
import json
|
|
||||||
import requests
|
|
||||||
import base64
|
import base64
|
||||||
import urllib.parse
|
import json
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
import requests
|
||||||
from flask.app import Flask
|
from flask.app import Flask
|
||||||
from flask.testing import FlaskClient
|
|
||||||
|
|
||||||
from keycloak.authorization import Authorization # type: ignore
|
from keycloak.authorization import Authorization # type: ignore
|
||||||
from keycloak.keycloak_openid import KeycloakOpenID # type: ignore
|
from keycloak.keycloak_openid import KeycloakOpenID # type: ignore
|
||||||
from keycloak.uma_permissions import AuthStatus # type: ignore
|
from keycloak.uma_permissions import AuthStatus # type: ignore
|
||||||
|
|
||||||
from tests.spiffworkflow_backend.integration.base_test import BaseTest
|
from tests.spiffworkflow_backend.integration.base_test import BaseTest
|
||||||
from spiffworkflow_backend.services.authentication_service import PublicAuthenticationService, KeycloakAuthenticationService
|
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
|
||||||
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
|
|
||||||
|
|
||||||
class TestAuthentication(BaseTest):
|
class TestAuthentication(BaseTest):
|
||||||
|
"""TestAuthentication."""
|
||||||
|
|
||||||
# def test_get_basic_token(self, app: Flask) -> None:
|
# def test_get_basic_token(self, app: Flask) -> None:
|
||||||
# for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
# for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
||||||
@ -35,34 +27,45 @@ class TestAuthentication(BaseTest):
|
|||||||
# assert isinstance(basic_token['scope'], str)
|
# assert isinstance(basic_token['scope'], str)
|
||||||
|
|
||||||
def test_get_token_script(self, app: Flask) -> None:
|
def test_get_token_script(self, app: Flask) -> None:
|
||||||
|
"""Test_get_token_script."""
|
||||||
print("Test Get Token Script")
|
print("Test Get Token Script")
|
||||||
|
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = self.get_keycloak_constants(app)
|
(
|
||||||
keycloak_user = 'ciuser1'
|
keycloak_server_url,
|
||||||
keycloak_pass = 'ciuser1'
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = self.get_keycloak_constants(app)
|
||||||
|
keycloak_user = "ciuser1"
|
||||||
|
keycloak_pass = "ciuser1"
|
||||||
|
|
||||||
print(f"Test Get Token Script: keycloak_server_url: {keycloak_server_url}")
|
print(f"Test Get Token Script: keycloak_server_url: {keycloak_server_url}")
|
||||||
print(f"Test Get Token Script: keycloak_client_id: {keycloak_client_id}")
|
print(f"Test Get Token Script: keycloak_client_id: {keycloak_client_id}")
|
||||||
print(f"Test Get Token Script: keycloak_realm_name: {keycloak_realm_name}")
|
print(f"Test Get Token Script: keycloak_realm_name: {keycloak_realm_name}")
|
||||||
print(f"Test Get Token Script: keycloak_client_secret_key: {keycloak_client_secret_key}")
|
print(
|
||||||
|
f"Test Get Token Script: keycloak_client_secret_key: {keycloak_client_secret_key}"
|
||||||
|
)
|
||||||
|
|
||||||
frontend_client_id = 'spiffworkflow-frontend'
|
frontend_client_id = "spiffworkflow-frontend"
|
||||||
|
|
||||||
print(f"Test Get Token Script: frontend_client_id: {frontend_client_id}")
|
print(f"Test Get Token Script: frontend_client_id: {frontend_client_id}")
|
||||||
|
|
||||||
# Get frontend token
|
# Get frontend token
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||||
post_data = {'grant_type': 'password',
|
post_data = {
|
||||||
'username': keycloak_user,
|
"grant_type": "password",
|
||||||
'password': keycloak_pass,
|
"username": keycloak_user,
|
||||||
'client_id': frontend_client_id
|
"password": keycloak_pass,
|
||||||
|
"client_id": frontend_client_id,
|
||||||
}
|
}
|
||||||
print(f"Test Get Token Script: request_url: {request_url}")
|
print(f"Test Get Token Script: request_url: {request_url}")
|
||||||
print(f"Test Get Token Script: headers: {headers}")
|
print(f"Test Get Token Script: headers: {headers}")
|
||||||
print(f"Test Get Token Script: post_data: {post_data}")
|
print(f"Test Get Token Script: post_data: {post_data}")
|
||||||
|
|
||||||
frontend_response = requests.post(request_url, headers=headers, json=post_data, data=post_data)
|
frontend_response = requests.post(
|
||||||
|
request_url, headers=headers, json=post_data, data=post_data
|
||||||
|
)
|
||||||
frontend_token = json.loads(frontend_response.text)
|
frontend_token = json.loads(frontend_response.text)
|
||||||
|
|
||||||
print(f"Test Get Token Script: frontend_response: {frontend_response}")
|
print(f"Test Get Token Script: frontend_response: {frontend_response}")
|
||||||
@ -77,37 +80,44 @@ class TestAuthentication(BaseTest):
|
|||||||
|
|
||||||
# Get backend token
|
# Get backend token
|
||||||
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
BACKEND_BASIC_AUTH_STRING = f"{keycloak_client_id}:{keycloak_client_secret_key}"
|
||||||
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding='ascii')
|
BACKEND_BASIC_AUTH_BYTES = bytes(BACKEND_BASIC_AUTH_STRING, encoding="ascii")
|
||||||
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
BACKEND_BASIC_AUTH = base64.b64encode(BACKEND_BASIC_AUTH_BYTES)
|
||||||
|
|
||||||
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/token"
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}"}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:token-exchange',
|
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}",
|
||||||
'client_id': keycloak_client_id,
|
}
|
||||||
"subject_token": frontend_token['access_token'],
|
data = {
|
||||||
"audience": keycloak_client_id}
|
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
||||||
|
"client_id": keycloak_client_id,
|
||||||
|
"subject_token": frontend_token["access_token"],
|
||||||
|
"audience": keycloak_client_id,
|
||||||
|
}
|
||||||
print(f"Test Get Token Script: request_url: {request_url}")
|
print(f"Test Get Token Script: request_url: {request_url}")
|
||||||
print(f"Test Get Token Script: headers: {headers}")
|
print(f"Test Get Token Script: headers: {headers}")
|
||||||
print(f"Test Get Token Script: data: {data}")
|
print(f"Test Get Token Script: data: {data}")
|
||||||
|
|
||||||
backend_response = requests.post(request_url, headers=headers, data=data)
|
backend_response = requests.post(request_url, headers=headers, data=data)
|
||||||
json_data = json.loads(backend_response.text)
|
json_data = json.loads(backend_response.text)
|
||||||
backend_token = json_data['access_token']
|
backend_token = json_data["access_token"]
|
||||||
print(f"Test Get Token Script: backend_response: {backend_response}")
|
print(f"Test Get Token Script: backend_response: {backend_response}")
|
||||||
print(f"Test Get Token Script: backend_token: {backend_token}")
|
print(f"Test Get Token Script: backend_token: {backend_token}")
|
||||||
|
|
||||||
if backend_token:
|
if backend_token:
|
||||||
# Getting resource set
|
# Getting resource set
|
||||||
auth_bearer_string = f"Bearer {backend_token}"
|
auth_bearer_string = f"Bearer {backend_token}"
|
||||||
headers = {"Content-Type": "application/json",
|
headers = {
|
||||||
"Authorization": auth_bearer_string}
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": auth_bearer_string,
|
||||||
|
}
|
||||||
|
|
||||||
# URI_TO_TEST_AGAINST = "%2Fprocess-models"
|
# URI_TO_TEST_AGAINST = "%2Fprocess-models"
|
||||||
URI_TO_TEST_AGAINST = "/status"
|
URI_TO_TEST_AGAINST = "/status"
|
||||||
request_url = \
|
request_url = (
|
||||||
f"{keycloak_server_url}/realms/{keycloak_realm_name}/authz/protection/resource_set?" + \
|
f"{keycloak_server_url}/realms/{keycloak_realm_name}/authz/protection/resource_set?"
|
||||||
f"matchingUri=true&deep=true&max=-1&exactName=false&uri={URI_TO_TEST_AGAINST}"
|
+ f"matchingUri=true&deep=true&max=-1&exactName=false&uri={URI_TO_TEST_AGAINST}"
|
||||||
|
)
|
||||||
# f"uri={URI_TO_TEST_AGAINST}"
|
# f"uri={URI_TO_TEST_AGAINST}"
|
||||||
print(f"Test Get Token Script: request_url: {request_url}")
|
print(f"Test Get Token Script: request_url: {request_url}")
|
||||||
print(f"Test Get Token Script: headers: {headers}")
|
print(f"Test Get Token Script: headers: {headers}")
|
||||||
@ -118,35 +128,43 @@ class TestAuthentication(BaseTest):
|
|||||||
json_data = json.loads(resource_result.text)
|
json_data = json.loads(resource_result.text)
|
||||||
resource_id_name_pairs = []
|
resource_id_name_pairs = []
|
||||||
for result in json_data:
|
for result in json_data:
|
||||||
if '_id' in result and result['_id']:
|
if "_id" in result and result["_id"]:
|
||||||
pair_key = result['_id']
|
pair_key = result["_id"]
|
||||||
if 'name' in result and result['name']:
|
if "name" in result and result["name"]:
|
||||||
pair_value = result['name']
|
pair_value = result["name"]
|
||||||
# pair = {{result['_id']}: {}}
|
# pair = {{result['_id']}: {}}
|
||||||
else:
|
else:
|
||||||
pair_value = 'no_name'
|
pair_value = "no_name"
|
||||||
# pair = {{result['_id']}: }
|
# pair = {{result['_id']}: }
|
||||||
pair = [pair_key, pair_value]
|
pair = [pair_key, pair_value]
|
||||||
resource_id_name_pairs.append(pair)
|
resource_id_name_pairs.append(pair)
|
||||||
print(f"Test Get Token Script: resource_id_name_pairs: {resource_id_name_pairs}")
|
print(
|
||||||
|
f"Test Get Token Script: resource_id_name_pairs: {resource_id_name_pairs}"
|
||||||
|
)
|
||||||
|
|
||||||
# Getting Permissions
|
# Getting Permissions
|
||||||
for resource_id_name_pair in resource_id_name_pairs:
|
for resource_id_name_pair in resource_id_name_pairs:
|
||||||
resource_id = resource_id_name_pair[0]
|
resource_id = resource_id_name_pair[0]
|
||||||
resource_name = resource_id_name_pair[1]
|
resource_id_name_pair[1]
|
||||||
|
|
||||||
headers = {"Content-Type": "application/x-www-form-urlencoded",
|
headers = {
|
||||||
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}"}
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"Authorization": f"Basic {BACKEND_BASIC_AUTH.decode('utf-8')}",
|
||||||
|
}
|
||||||
|
|
||||||
post_data = {"audience": keycloak_client_id,
|
post_data = {
|
||||||
|
"audience": keycloak_client_id,
|
||||||
"permission": resource_id,
|
"permission": resource_id,
|
||||||
"subject_token": backend_token,
|
"subject_token": backend_token,
|
||||||
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket"}
|
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
|
||||||
|
}
|
||||||
print(f"Test Get Token Script: headers: {headers}")
|
print(f"Test Get Token Script: headers: {headers}")
|
||||||
print(f"Test Get Token Script: post_data: {post_data}")
|
print(f"Test Get Token Script: post_data: {post_data}")
|
||||||
print(f"Test Get Token Script: request_url: {request_url}")
|
print(f"Test Get Token Script: request_url: {request_url}")
|
||||||
|
|
||||||
permission_result = requests.post(request_url, headers=headers, data=post_data)
|
permission_result = requests.post(
|
||||||
|
request_url, headers=headers, data=post_data
|
||||||
|
)
|
||||||
print(f"Test Get Token Script: permission_result: {permission_result}")
|
print(f"Test Get Token Script: permission_result: {permission_result}")
|
||||||
|
|
||||||
print("test_get_token_script")
|
print("test_get_token_script")
|
||||||
@ -155,6 +173,7 @@ class TestAuthentication(BaseTest):
|
|||||||
# keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = self.get_keycloak_constants(app)
|
# keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = self.get_keycloak_constants(app)
|
||||||
# request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/auth"
|
# request_url = f"{keycloak_server_url}/realms/{keycloak_realm_name}/protocol/openid-connect/auth"
|
||||||
|
|
||||||
|
|
||||||
# class TestOtherStuff(BaseTest):
|
# class TestOtherStuff(BaseTest):
|
||||||
#
|
#
|
||||||
# # def test_get_backend_token(self, app: Flask) -> None:
|
# # def test_get_backend_token(self, app: Flask) -> None:
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
"""Test_authorization."""
|
"""Test_authorization."""
|
||||||
import requests # type: ignore
|
import requests # type: ignore
|
||||||
from flask.app import Flask
|
from flask.app import Flask
|
||||||
from flask.testing import FlaskClient
|
|
||||||
|
|
||||||
from tests.spiffworkflow_backend.integration.base_test import BaseTest
|
from tests.spiffworkflow_backend.integration.base_test import BaseTest
|
||||||
|
|
||||||
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
from spiffworkflow_backend.services.authorization_service import AuthorizationService
|
||||||
@ -12,82 +10,95 @@ from spiffworkflow_backend.services.authorization_service import AuthorizationSe
|
|||||||
|
|
||||||
class TestAuthorization(BaseTest):
|
class TestAuthorization(BaseTest):
|
||||||
"""TestAuthorization."""
|
"""TestAuthorization."""
|
||||||
|
|
||||||
def test_get_bearer_token(self, app: Flask) -> None:
|
def test_get_bearer_token(self, app: Flask) -> None:
|
||||||
for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
"""Test_get_bearer_token."""
|
||||||
|
for user_id in ("user_1", "user_2", "admin_1", "admin_2"):
|
||||||
public_access_token = self.get_public_access_token(user_id, user_id)
|
public_access_token = self.get_public_access_token(user_id, user_id)
|
||||||
bearer_token = AuthorizationService().get_bearer_token(public_access_token)
|
bearer_token = AuthorizationService().get_bearer_token(public_access_token)
|
||||||
assert isinstance(public_access_token, str)
|
assert isinstance(public_access_token, str)
|
||||||
assert isinstance(bearer_token, dict)
|
assert isinstance(bearer_token, dict)
|
||||||
assert 'access_token' in bearer_token
|
assert "access_token" in bearer_token
|
||||||
assert isinstance(bearer_token['access_token'], str)
|
assert isinstance(bearer_token["access_token"], str)
|
||||||
assert 'refresh_token' in bearer_token
|
assert "refresh_token" in bearer_token
|
||||||
assert isinstance(bearer_token['refresh_token'], str)
|
assert isinstance(bearer_token["refresh_token"], str)
|
||||||
assert 'token_type' in bearer_token
|
assert "token_type" in bearer_token
|
||||||
assert bearer_token['token_type'] == 'Bearer'
|
assert bearer_token["token_type"] == "Bearer"
|
||||||
assert 'scope' in bearer_token
|
assert "scope" in bearer_token
|
||||||
assert isinstance(bearer_token['scope'], str)
|
assert isinstance(bearer_token["scope"], str)
|
||||||
|
|
||||||
def test_get_user_info_from_public_access_token(self, app: Flask) -> None:
|
def test_get_user_info_from_public_access_token(self, app: Flask) -> None:
|
||||||
for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
"""Test_get_user_info_from_public_access_token."""
|
||||||
|
for user_id in ("user_1", "user_2", "admin_1", "admin_2"):
|
||||||
public_access_token = self.get_public_access_token(user_id, user_id)
|
public_access_token = self.get_public_access_token(user_id, user_id)
|
||||||
user_info = AuthorizationService().get_user_info_from_public_access_token(public_access_token)
|
user_info = AuthorizationService().get_user_info_from_public_access_token(
|
||||||
assert 'sub' in user_info
|
public_access_token
|
||||||
assert isinstance(user_info['sub'], str)
|
)
|
||||||
assert len(user_info['sub']) == 36
|
assert "sub" in user_info
|
||||||
assert 'preferred_username' in user_info
|
assert isinstance(user_info["sub"], str)
|
||||||
assert user_info['preferred_username'] == user_id
|
assert len(user_info["sub"]) == 36
|
||||||
assert 'email' in user_info
|
assert "preferred_username" in user_info
|
||||||
assert user_info['email'] == f"{user_id}@example.com"
|
assert user_info["preferred_username"] == user_id
|
||||||
|
assert "email" in user_info
|
||||||
|
assert user_info["email"] == f"{user_id}@example.com"
|
||||||
|
|
||||||
def test_introspect_token(self, app: Flask) -> None:
|
def test_introspect_token(self, app: Flask) -> None:
|
||||||
keycloak_server_url, keycloak_client_id, keycloak_realm_name, keycloak_client_secret_key = self.get_keycloak_constants(app)
|
"""Test_introspect_token."""
|
||||||
for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
(
|
||||||
|
keycloak_server_url,
|
||||||
|
keycloak_client_id,
|
||||||
|
keycloak_realm_name,
|
||||||
|
keycloak_client_secret_key,
|
||||||
|
) = self.get_keycloak_constants(app)
|
||||||
|
for user_id in ("user_1", "user_2", "admin_1", "admin_2"):
|
||||||
basic_token = self.get_public_access_token(user_id, user_id)
|
basic_token = self.get_public_access_token(user_id, user_id)
|
||||||
introspection = AuthorizationService().introspect_token(basic_token)
|
introspection = AuthorizationService().introspect_token(basic_token)
|
||||||
assert isinstance(introspection, dict)
|
assert isinstance(introspection, dict)
|
||||||
assert introspection['typ'] == 'Bearer'
|
assert introspection["typ"] == "Bearer"
|
||||||
assert introspection['preferred_username'] == user_id
|
assert introspection["preferred_username"] == user_id
|
||||||
assert introspection['client_id'] == 'spiffworkflow-frontend'
|
assert introspection["client_id"] == "spiffworkflow-frontend"
|
||||||
|
|
||||||
assert 'resource_access' in introspection
|
assert "resource_access" in introspection
|
||||||
resource_access = introspection['resource_access']
|
resource_access = introspection["resource_access"]
|
||||||
assert isinstance(resource_access, dict)
|
assert isinstance(resource_access, dict)
|
||||||
|
|
||||||
assert keycloak_client_id in resource_access
|
assert keycloak_client_id in resource_access
|
||||||
client = resource_access[keycloak_client_id]
|
client = resource_access[keycloak_client_id]
|
||||||
assert 'roles' in client
|
assert "roles" in client
|
||||||
roles = client['roles']
|
roles = client["roles"]
|
||||||
|
|
||||||
assert isinstance(roles, list)
|
assert isinstance(roles, list)
|
||||||
if user_id == 'admin_1':
|
if user_id == "admin_1":
|
||||||
assert len(roles) == 2
|
assert len(roles) == 2
|
||||||
for role in roles:
|
for role in roles:
|
||||||
assert role in ('User', 'Admin')
|
assert role in ("User", "Admin")
|
||||||
elif user_id == 'admin_2':
|
elif user_id == "admin_2":
|
||||||
assert len(roles) == 1
|
assert len(roles) == 1
|
||||||
assert roles[0] == 'User'
|
assert roles[0] == "User"
|
||||||
elif user_id == 'user_1' or user_id == 'user_2':
|
elif user_id == "user_1" or user_id == "user_2":
|
||||||
assert len(roles) == 2
|
assert len(roles) == 2
|
||||||
for role in roles:
|
for role in roles:
|
||||||
assert role in ('User', 'Anonymous')
|
assert role in ("User", "Anonymous")
|
||||||
|
|
||||||
def test_get_permission_by_token(self, app: Flask) -> None:
|
def test_get_permission_by_token(self, app: Flask) -> None:
|
||||||
resource_names = 'Default Resource', 'Process Groups', 'Process Models'
|
"""Test_get_permission_by_token."""
|
||||||
output = {}
|
output = {}
|
||||||
for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
for user_id in ("user_1", "user_2", "admin_1", "admin_2"):
|
||||||
output[user_id] = {}
|
output[user_id] = {}
|
||||||
basic_token = self.get_public_access_token(user_id, user_id)
|
basic_token = self.get_public_access_token(user_id, user_id)
|
||||||
permissions = AuthorizationService().get_permission_by_basic_token(basic_token)
|
permissions = AuthorizationService().get_permission_by_basic_token(
|
||||||
|
basic_token
|
||||||
|
)
|
||||||
if isinstance(permissions, list):
|
if isinstance(permissions, list):
|
||||||
for permission in permissions:
|
for permission in permissions:
|
||||||
resource_name = permission['rsname']
|
resource_name = permission["rsname"]
|
||||||
output[user_id][resource_name] = {}
|
output[user_id][resource_name] = {}
|
||||||
# assert resource_name in resource_names
|
# assert resource_name in resource_names
|
||||||
# if resource_name == 'Process Groups' or resource_name == 'Process Models':
|
# if resource_name == 'Process Groups' or resource_name == 'Process Models':
|
||||||
if 'scopes' in permission:
|
if "scopes" in permission:
|
||||||
# assert 'scopes' in permission
|
# assert 'scopes' in permission
|
||||||
scopes = permission['scopes']
|
scopes = permission["scopes"]
|
||||||
output[user_id][resource_name]['scopes'] = scopes
|
output[user_id][resource_name]["scopes"] = scopes
|
||||||
# assert isinstance(scopes, list)
|
# assert isinstance(scopes, list)
|
||||||
# assert len(scopes) == 1
|
# assert len(scopes) == 1
|
||||||
# assert scopes[0] == 'read'
|
# assert scopes[0] == 'read'
|
||||||
@ -128,15 +139,16 @@ class TestAuthorization(BaseTest):
|
|||||||
print("test_get_permission_by_token")
|
print("test_get_permission_by_token")
|
||||||
|
|
||||||
def test_get_auth_status_for_resource_and_scope_by_token(self, app: Flask) -> None:
|
def test_get_auth_status_for_resource_and_scope_by_token(self, app: Flask) -> None:
|
||||||
resources = 'Admin', 'Process Groups', 'Process Models'
|
"""Test_get_auth_status_for_resource_and_scope_by_token."""
|
||||||
|
resources = "Admin", "Process Groups", "Process Models"
|
||||||
# scope = 'read'
|
# scope = 'read'
|
||||||
output = {}
|
output = {}
|
||||||
for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
for user_id in ("user_1", "user_2", "admin_1", "admin_2"):
|
||||||
output[user_id] = {}
|
output[user_id] = {}
|
||||||
basic_token = self.get_public_access_token(user_id, user_id)
|
basic_token = self.get_public_access_token(user_id, user_id)
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
output[user_id][resource] = {}
|
output[user_id][resource] = {}
|
||||||
for scope in 'instantiate', 'read', 'update', 'delete':
|
for scope in "instantiate", "read", "update", "delete":
|
||||||
auth_status = AuthorizationService().get_auth_status_for_resource_and_scope_by_token(
|
auth_status = AuthorizationService().get_auth_status_for_resource_and_scope_by_token(
|
||||||
basic_token, resource, scope
|
basic_token, resource, scope
|
||||||
)
|
)
|
||||||
@ -144,20 +156,21 @@ class TestAuthorization(BaseTest):
|
|||||||
print("test_get_auth_status_for_resource_and_scope_by_token")
|
print("test_get_auth_status_for_resource_and_scope_by_token")
|
||||||
|
|
||||||
def test_get_permissions_by_token_for_resource_and_scope(self, app: Flask):
|
def test_get_permissions_by_token_for_resource_and_scope(self, app: Flask):
|
||||||
resource_names = 'Default Resource', 'Process Groups', 'Process Models'
|
"""Test_get_permissions_by_token_for_resource_and_scope."""
|
||||||
|
resource_names = "Default Resource", "Process Groups", "Process Models"
|
||||||
output = {}
|
output = {}
|
||||||
for user_id in ('user_1', 'user_2', 'admin_1', 'admin_2'):
|
for user_id in ("user_1", "user_2", "admin_1", "admin_2"):
|
||||||
output[user_id] = {}
|
output[user_id] = {}
|
||||||
basic_token = self.get_public_access_token(user_id, user_id)
|
basic_token = self.get_public_access_token(user_id, user_id)
|
||||||
for resource in resource_names:
|
for resource in resource_names:
|
||||||
output[user_id][resource] = {}
|
output[user_id][resource] = {}
|
||||||
for scope in 'instantiate', 'read', 'update', 'delete':
|
for scope in "instantiate", "read", "update", "delete":
|
||||||
permissions = AuthorizationService().\
|
permissions = AuthorizationService().get_permissions_by_token_for_resource_and_scope(
|
||||||
get_permissions_by_token_for_resource_and_scope(basic_token, resource, scope)
|
basic_token, resource, scope
|
||||||
|
)
|
||||||
output[user_id][resource][scope] = permissions
|
output[user_id][resource][scope] = permissions
|
||||||
print("test_get_permissions_by_token_for_resource_and_scope")
|
print("test_get_permissions_by_token_for_resource_and_scope")
|
||||||
|
|
||||||
|
|
||||||
# # def test_authorize_action(self, app: Flask, client: FlaskClient) -> None:
|
# # def test_authorize_action(self, app: Flask, client: FlaskClient) -> None:
|
||||||
# # action = 'my_action'
|
# # action = 'my_action'
|
||||||
# # result = app.get(f'/authorize/{action}')
|
# # result = app.get(f'/authorize/{action}')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user