merged in main and resolved conflicts

This commit is contained in:
jasquat 2022-10-11 10:18:20 -04:00
commit e6cc68fc9d
5 changed files with 142 additions and 88 deletions

View File

@ -8,8 +8,9 @@ servers:
- url: http://localhost:5000/v1.0
security:
- jwt: ["secret"]
# - oAuth2AuthCode:
# - read_email
- oAuth2AuthCode:
- read_email
- uid
paths:
/login:
@ -85,47 +86,41 @@ paths:
"200":
description: Logout Authenticated User
# /login_api:
# parameters:
# - name: redirect_url
# in: query
# required: false
# schema:
# type: string
# get:
# security: []
# operationId: spiffworkflow_backend.routes.user.login_api
# summary: Authenticate user for API access
# tags:
# - Authentication
# responses:
# "304":
# description: Redirection to the hosted frontend with an auth_token header.
# /login_api_return:
# parameters:
# - name: code
# in: query
# required: true
# schema:
# type: string
# - name: state
# in: query
# required: true
# schema:
# type: string
# - name: session_state
# in: query
# required: false
# schema:
# type: string
# get:
# security: []
# operationId: spiffworkflow_backend.routes.user.login_api_return
# tags:
# - Authentication
# responses:
# "200":
# description: Test Return Response
/login_api:
get:
security: []
operationId: spiffworkflow_backend.routes.user.login_api
summary: Authenticate user for API access
tags:
- Authentication
responses:
"200":
description: Redirects to authentication server
/login_api_return:
parameters:
- name: code
in: query
required: true
schema:
type: string
- name: state
in: query
required: true
schema:
type: string
- name: session_state
in: query
required: false
schema:
type: string
get:
security: []
operationId: spiffworkflow_backend.routes.user.login_api_return
tags:
- Authentication
responses:
"200":
description: Test Return Response
/status:
get:
@ -1250,10 +1245,20 @@ components:
flows:
authorizationCode:
authorizationUrl: /v1.0/login_api
tokenUrl: /v1.0/login_return
tokenUrl: /v1.0/login_api_return
scopes:
read_email: read email
x-tokenInfoFunc: spiffworkflow_backend.routes.user.get_scope
# oAuth2AuthCode:
# type: oauth2
# description: authenticate with openid server
# flows:
# implicit:
# authorizationUrl: /v1.0/login_api
# scopes:
# uid: uid
# x-tokenInfoUrl: localhost:7000/v1.0/login_api_return
# x-tokenInfoFunc: spiffworkflow_backend.routes.user.get_scope
schemas:
OkTrue:

View File

@ -206,9 +206,11 @@ def process_model_update(
process_group_id: str, process_model_id: str, body: Dict[str, Union[str, bool, int]]
) -> Any:
"""Process_model_update."""
body_include_list = ["display_name"]
body_include_list = ["display_name", "primary_file_name", "primary_process_id"]
body_filtered = {
include_item: body[include_item] for include_item in body_include_list
include_item: body[include_item]
for include_item in body_include_list
if include_item in body
}
process_model = get_process_model(process_model_id, process_group_id)

View File

@ -214,49 +214,86 @@ def login_return(code: str, state: str, session_state: str) -> Optional[Response
state_redirect_url = state_dict["redirect_url"]
id_token_object = PublicAuthenticationService().get_id_token_object(code)
id_token = id_token_object["id_token"]
if "id_token" in id_token_object:
id_token = id_token_object["id_token"]
if PublicAuthenticationService.validate_id_token(id_token):
user_info = PublicAuthenticationService.get_user_info_from_id_token(
id_token_object["access_token"]
)
if user_info and "error" not in user_info:
user_model = (
UserModel.query.filter(UserModel.service == "open_id")
.filter(UserModel.service_id == user_info["sub"])
.first()
if PublicAuthenticationService.validate_id_token(id_token):
user_info = PublicAuthenticationService.get_user_info_from_id_token(
id_token_object["access_token"]
)
if user_model is None:
current_app.logger.debug("create_user in login_return")
name = username = email = ""
if "name" in user_info:
name = user_info["name"]
if "username" in user_info:
username = user_info["username"]
elif "preferred_username" in user_info:
username = user_info["preferred_username"]
if "email" in user_info:
email = user_info["email"]
user_model = UserService.create_user(
service="open_id",
service_id=user_info["sub"],
name=name,
username=username,
email=email,
if user_info and "error" not in user_info:
user_model = (
UserModel.query.filter(UserModel.service == "open_id")
.filter(UserModel.service_id == user_info["sub"])
.first()
)
if user_model:
g.user = user_model.id
if user_model is None:
current_app.logger.debug("create_user in login_return")
name = username = email = ""
if "name" in user_info:
name = user_info["name"]
if "username" in user_info:
username = user_info["username"]
elif "preferred_username" in user_info:
username = user_info["preferred_username"]
if "email" in user_info:
email = user_info["email"]
user_model = UserService().create_user(
service="open_id",
service_id=user_info["sub"],
name=name,
username=username,
email=email,
)
redirect_url = (
f"{state_redirect_url}?"
+ f"access_token={id_token_object['access_token']}&"
+ f"id_token={id_token}"
)
return redirect(redirect_url)
raise ApiError(
code="invalid_login", message="Login failed. Please try again", status_code=401
if user_model:
g.user = user_model.id
redirect_url = (
f"{state_redirect_url}?"
+ f"access_token={id_token_object['access_token']}&"
+ f"id_token={id_token}"
)
return redirect(redirect_url)
raise ApiError(
code="invalid_login",
message="Login failed. Please try again",
status_code=401,
)
else:
raise ApiError(
code="invalid_token",
message="Login failed. Please try again",
status_code=401,
)
def login_api() -> Response:
"""Login_api."""
redirect_url = "/v1.0/login_api_return"
state = PublicAuthenticationService.generate_state(redirect_url)
login_redirect_url = PublicAuthenticationService().get_login_redirect_url(
state.decode("UTF-8"), redirect_url
)
return redirect(login_redirect_url)
def login_api_return(code: str, state: str, session_state: str) -> str:
"""Login_api_return."""
state_dict = ast.literal_eval(base64.b64decode(state).decode("utf-8"))
state_dict["redirect_url"]
id_token_object = PublicAuthenticationService().get_id_token_object(
code, "/v1.0/login_api_return"
)
access_token: str = id_token_object["access_token"]
assert access_token # noqa: S101
return access_token
# return redirect("localhost:7000/v1.0/ui")
# return {'uid': 'user_1'}
def logout(id_token: str, redirect_url: Optional[str]) -> Response:

View File

@ -114,7 +114,9 @@ class PublicAuthenticationService:
state = base64.b64encode(bytes(str({"redirect_url": redirect_url}), "UTF-8"))
return state
def get_login_redirect_url(self, state: str) -> str:
def get_login_redirect_url(
self, state: str, redirect_url: str = "/v1.0/login_return"
) -> str:
"""Get_login_redirect_url."""
(
open_id_server_url,
@ -122,7 +124,7 @@ class PublicAuthenticationService:
open_id_realm_name,
open_id_client_secret_key,
) = PublicAuthenticationService.get_open_id_args()
return_redirect_url = f"{self.get_backend_url()}/v1.0/login_return"
return_redirect_url = f"{self.get_backend_url()}{redirect_url}"
login_redirect_url = (
f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/auth?"
+ f"state={state}&"
@ -133,7 +135,9 @@ class PublicAuthenticationService:
)
return login_redirect_url
def get_id_token_object(self, code: str) -> dict:
def get_id_token_object(
self, code: str, redirect_url: str = "/v1.0/login_return"
) -> dict:
"""Get_id_token_object."""
(
open_id_server_url,
@ -152,7 +156,7 @@ class PublicAuthenticationService:
data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": f"{self.get_backend_url()}/v1.0/login_return",
"redirect_uri": f"{self.get_backend_url()}{redirect_url}",
}
request_url = f"{open_id_server_url}/realms/{open_id_realm_name}/protocol/openid-connect/token"

View File

@ -179,9 +179,13 @@ class TestProcessApi(BaseTest):
assert process_model.id == "make_cookies"
assert process_model.display_name == "Cooooookies"
assert process_model.is_review is False
assert process_model.primary_file_name is None
assert process_model.primary_process_id is None
process_model.display_name = "Updated Display Name"
process_model.is_review = True
process_model.primary_file_name = "superduper.bpmn"
process_model.primary_process_id = "superduper"
process_model.is_review = True # not in the include list, so get ignored
user = self.find_or_create_user()
response = client.put(
@ -193,6 +197,8 @@ class TestProcessApi(BaseTest):
assert response.status_code == 200
assert response.json is not None
assert response.json["display_name"] == "Updated Display Name"
assert response.json["primary_file_name"] == "superduper.bpmn"
assert response.json["primary_process_id"] == "superduper"
assert response.json["is_review"] is False
def test_process_model_list(