marcospereirampj / python-keycloak

MIT License
728 stars 303 forks source link

Fine Grained Permissions #436

Open andez2000 opened 1 year ago

andez2000 commented 1 year ago

I am trying to setup fine grained permissions for authorization in my client. I am not sure if I am doing things right in here - or in the python keycloak api.

I am running keycloak in docker on Windows 10, using FastAPI and Python 3.11.3 to consume it. I have tried to configure a single check which I want to consume in my end point to basically ask the question can Bob perform this action?

The question is how do I ask this question using python-keycloak?

I have some code which I am testing via Postman. The token is retrieved fine and passed to my Fast API end point.

Code

access_token = request.headers.get("Authorization").replace("Bearer ", "")

token_info = keycloak_openid.introspect(access_token)

token_info['realm_access']['roles'] gives me the roles bob is assigned to.

Where do I need to look to verify the fine grained permissions?

Configuration

Realm

Name: AcmeRealm

Realm Roles

Name: bobs role

Realm Groups

Name: Bobs Role Mapping: bobs role Members: Bob

Client

ID: acme.app

Capability Config

Client Authentication: On Authorization: On Authentication Flow: Standard Flow, Direct Access Grants, Service Accounts Roles

Authorization

Resource

Name: bobs.resource Owner: acme.app Authorization Scopes: acme.app.scope

Authorization Scope

Name: acme.app.scope

Policy

Name: bobs policy Roles: bobs role Logic: Positive

Resource Based Permission

Name: some_permission Apply to resource type: No Resources: bobs.resource Policies: bobs policy Decision Strategy: Affirmative

Scope Based Permission

Name: bobs.scopebasedresource.permission Apply to resource type: No Resources: Authorization scopes: acme.app.scope Policies: bobs.policy Decision strategy: Affirmative

What have I tried?

I have had no luck calling these methods below - as I am still new and trying to figure things out - and parameters are unknown as I am unclear.

permissionsxx = keycloak_openid.uma_permissions(
 access_token,
 permissions="bobs.resourceacme.app.scope"
)

rpt = keycloak_openid.entitlement(
 access_token,
 "bobs.resource"
)

policies = keycloak_openid.get_policies(
 access_token,
 method_token_info='decode',
 key="KEYCLOAK_PUBLIC_KEY" # 
)

permissionsxxxx = keycloak_openid.get_permissions(
 access_token,
 method_token_info='introspect'
)

Any help appreciated.

Thanks

namoshizun commented 1 year ago

PS: Not really an answer because I too haven't fully figured this out...

According to the doc, you will need to call keycloak_openid.load_authorization_config("example-authz-config.json") before invoking keycloak_openid.get_permissions or keycloak_openid.get_policies, where the auth config file can be exported via the Keycloak UI (see this issue).

What I don't understand is the reason for loading the auth config in the first place. Intuitively, shouldn't the Client's authorization settings be retrieved via the Keycloak API instead of from a static file?

andez2000 commented 1 year ago

I finally got a fully working fine grained (Attribute Based Access Control) up and running with FastAPI/Python. I will try and migrate it to my github.

import os
from fastapi import Request, HTTPException
from keycloak import KeycloakOpenID, KeycloakPostError

KEYCLOAK_URL = "..."
KEYCLOAK_REALM_NAME = "..."
KEYCLOAK_CLIENT_ID = "..."
KEYCLOAK_CLIENT_SECRET = "..."

keycloak_openid = KeycloakOpenID(
    server_url=f"{KEYCLOAK_URL}",
    realm_name=f"{KEYCLOAK_REALM_NAME}",
    client_id=f"{KEYCLOAK_CLIENT_ID}",
    client_secret_key=f"{KEYCLOAK_CLIENT_SECRET}"
)

def authorize_request(
    request: Request,
    permissions: str
):
    access_token = request.headers.get("whatever header you put the token in").split("Bearer ")[1]

    rpt = keycloak_openid.uma_permissions(
            access_token,
            permissions=permissions
        )
    # if you get here your all good.  add exception handling

This will work against the attached realm.json configuration you can import inside the keycloak ui (or during startup). realm.zip