diff --git a/README.md b/README.md index e0df669..5bc2819 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ cd um-identity-api 3. Execute - 3.1 Run with docker compose + 3.1 Run with docker compose (Identity API + Keycloak + Postgres) ```sh docker compose up -d --build ``` diff --git a/app/routers/clients_resources.py b/app/routers/clients_resources.py index 618c839..477ba6b 100644 --- a/app/routers/clients_resources.py +++ b/app/routers/clients_resources.py @@ -1,10 +1,12 @@ from typing import List -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException from app.keycloak_client import keycloak +from app.log import logger from app.models.policies import PolicyType from app.models.resources import Resource +from app.routers.resources import get_resources router = APIRouter( prefix="/{client_id}/resources", @@ -16,40 +18,73 @@ def register_resources(client_id: str, resources: List[Resource]): response_list = [] for resource in resources: - resource_name = resource.name.replace(" ", "_") - res = { - "name": resource_name, - "uris": resource.uris, - "scopes": resource.scopes, - } - response_resource = keycloak.register_resource(res, client_id) - response_list.append(response_resource) - permissions = resource.permissions - policy_list = [] - if permissions.role: - policy = { - "name": f'{resource_name}_role_policy', - "roles": [{"id": p} for p in permissions.role] + if resource.name.lower() == "default resource": + client_resources = get_resources(client_id) + default_resource = None + for client_resource in client_resources: + if client_resource["name"].lower() == "default resource": + default_resource = client_resource + if default_resource: + # update default resource + default_resource["scopes"] = resource.scopes + update_resource(client_id=client_id, resource_id=default_resource['_id'], resource=default_resource) + response_list.append(default_resource) + else: + # create default resource + res = { + "name": resource.name, + "uris": resource.uris, + "scopes": resource.scopes, + } + response_resource = keycloak.register_resource(res, client_id) + response_list.append(response_resource) + permission_payload = { + "type": "resource", + "name": f'{resource.name} Permission', + "decisionStrategy": "UNANIMOUS", + "resources": [ + resource.name + ], + "policies": ["Default Policy"] } - policy_response = keycloak.register_role_policy(policy, client_id) - policy_list.append(policy_response["name"]) - if permissions.user: - policy = { - "name": f'{resource_name}_user_policy', - "users": permissions.user + keycloak.create_client_authz_resource_based_permission(client_id, permission_payload) + else: + res = { + "name": resource.name, + "uris": resource.uris, + "scopes": resource.scopes, } - policy_response = keycloak.register_user_policy(policy, client_id) - policy_list.append(policy_response["name"]) - permission_payload = { - "type": "resource", - "name": f'{resource_name}_permission', - "decisionStrategy": resource.decisionStrategy, - "resources": [ - resource_name - ], - "policies": policy_list - } - keycloak.create_client_authz_resource_based_permission(client_id, permission_payload) + response_resource = keycloak.register_resource(res, client_id) + response_list.append(response_resource) + permissions = resource.permissions + policy_list = [] + if permissions.role: + policy = { + "name": f'{resource.name} Role Policy', + "roles": [{"id": p} for p in permissions.role] + } + policy_response = keycloak.register_role_policy(policy, client_id) + print(policy_response) + policy_list.append(policy_response["name"]) + if permissions.user: + policy = { + "name": f'{resource.name} User Policy', + "users": permissions.user + } + policy_response = keycloak.register_user_policy(policy, client_id) + print(policy_response) + policy_list.append(policy_response["name"]) + print(policy_list) + permission_payload = { + "type": "resource", + "name": f'{resource.name} Permission', + "decisionStrategy": resource.decisionStrategy, + "resources": [ + resource.name + ], + "policies": policy_list + } + keycloak.create_client_authz_resource_based_permission(client_id, permission_payload) return response_list @@ -59,17 +94,17 @@ def delete_resource_and_policies(client_id: str, resource_name: str): client_policies = keycloak.get_client_authz_policies(client_id) for policy in client_policies: for policy_type in [e.value for e in PolicyType]: - if policy['name'] == f'{resource_name}_{policy_type}_policy': + if policy['name'].lower() == f'{resource_name} {policy_type} policy'.lower(): keycloak.delete_policy(policy['id'], client_id) # delete permissions permissions = keycloak.get_client_resource_permissions(client_id) for permission in permissions: - if permission['name'] == f'{resource_name}_permission': + if permission['name'].lower() == f'{resource_name} permission'.lower(): keycloak.delete_resource_permissions(client_id, permission['id']) # delete resources resources = keycloak.get_resources(client_id) for resource in resources: - if resource['name'] == resource_name: + if resource['name'].lower() == resource_name.lower(): return keycloak.delete_resource(resource['_id'], client_id) diff --git a/app/routers/resources.py b/app/routers/resources.py index e20bd81..de89f04 100644 --- a/app/routers/resources.py +++ b/app/routers/resources.py @@ -9,8 +9,8 @@ @router.get("") -def get_resources(): - return keycloak.get_resources() +def get_resources(client_id: str): + return keycloak.get_resources(client_id) @router.get("/resources/{resource_id}") diff --git a/docker-compose.yml b/docker-compose.yml index 7d406f7..95d5e2b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,7 +29,8 @@ services: - KC_DB_PASSWORD=123456 - KC_DB_USERNAME=keycloak - KC_DB_URL_PORT=5432 - entrypoint: /opt/keycloak/bin/kc.sh start + - KC_FEATURES=account3,admin-fine-grained-authz,declarative-user-profile,recovery-codes,scripts + entrypoint: /opt/keycloak/bin/kc.sh start-dev restart: on-failure postgres: image: postgres:16.0