Using the "pas.plugins.memberpropertytogroup" addon, together "pas.plugins.oidc" addon for the Keycloak users

Is it possible to use the "pas.plugins.memberpropertytogroup" addon, together with the "pas.plugins.oidc" addon? the following requirements:

  • Let a Keycloak user log in as a Plone user and be assigned to a virtual group based on the member properties with the role "Member"? [0]

My question is because the "pas.plugins.memberpropertytogroup" addon documentation references to use this addon for "LDAP infrastructure" [1] in my case is a "Keycloak infrastructure".

[0] Create virtual groups based on member properties — PAS Plugin MemberPropertyToGroup 1.0a1 documentation
[1] pas.plugins.memberpropertytogroup — PAS Plugin MemberPropertyToGroup 1.0a1 documentation

we're using this, reading the property from an host header (SSO), maybe some code can be useful for your usecase.

1 Like

We hare using pas.plugins.oidc group assigning feature directly, without using pas.plugins.memberpropertytogroup. See:

1 Like

@erral How do you find the property "hd" when you using "Google Workspace" in the Plone request?

In my case:

I can obtain an access token; I made a curl request command:

curl --location --request POST 'http://localhost:8081/realms/plone/protocol/openid-connect/token' \
     --header 'Content-Type: application/x-www-form-urlencoded' \
     --header 'Accept: application/json' \
     --data-urlencode 'client_id=plone' \
     --data-urlencode 'grant_type=password' \
     --data-urlencode 'client_secret=ERf33TbCtfP7NunSiIbCscocE' \
     --data-urlencode 'scope=profile' \
     --data-urlencode 'username=lcaballero' \
     --data-urlencode 'password=TupeGqSshotTtouygr4sXnWNaqN'

The successful response shows the "access_token" if I decode it shows the following Payload:

{
  "exp": 1709628054,
  "iat": 1709627754,
  "jti": "1b24c7a2-6a36-471c-b86f-979bcaf6b9f5",
  "iss": "http://localhost:8081/realms/plone",
  "aud": "account",
  "sub": "ba317b32-3395-44c4-8ffe-6e2605b9f0c0",
  "typ": "Bearer",
  "azp": "plone",
  "session_state": "17463e76-3369-4ef1-8185-aadece3fd49b",
  "acr": "1",
  "allowed-origins": [
    "http://localhost:7080/Plone"
  ],
  "realm_access": {
    "roles": [
      "default-roles-plone",
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "email profile",
  "sid": "17463e76-3369-4ef1-8185-aadece3fd49b",
  "email_verified": true,
  "name": "Leonardo Caballero",
  "preferred_username": "lcaballero",
  "organization_name": "acme",
  "given_name": "Leonardo",
  "family_name": "Caballero",
  "email": "leonardocaballero@gmail.com"
}

That is the response from my Keycloak server, but my question is:

How to extract the "username" or the "email" properties from a Plone request?

This is not clear from the Plone 6 documentation, I am using Plone 6.0.7 version with Classic UI.

it looks like it is something specific for Google. You can use any other attribute that comes from the userinfo request.

You may also look at this PR that implements Keycloak group enumeration; Implement Keycloak Group Enumeration and Group Introspection by ericof · Pull Request #43 · collective/pas.plugins.oidc · GitHub

1 Like

@erral yes, you are right!

Can you help me verify the following code, to extract the user and password from the session?

My code:

        # Decode the Plone session cookie
        session_cookie = self.request.cookies.get('__ac')
        decoded_session = base64.b64decode(session_cookie).decode('utf-8')

        # Extract the username and password from the decoded session
        username, password = decoded_session.split(':')

I need to extract the user and password because the KeycloakOpenID class of the python-keycloak package needs the user and password logged in.

Here my full code:

 def get_user_info(self):
        """Get the user_info values"""

        from keycloak import KeycloakOpenID
        from keycloak.exceptions import KeycloakError
        from urllib.parse import urlparse as up
        import base64

        # Get PAS tool
        pas = api.portal.get_tool("acl_users")
        # Get Issuer
        issuer = pas.oidc.getProperty('issuer')
        # Get Client Id
        client_id = pas.oidc.getProperty('client_id')
        # Get Realm Name
        realm_name = up(issuer).__getattribute__('path').split('/')[-2]
        # Get Client Secret
        client_secret = pas.oidc.getProperty('client_secret')

        # Configure Keycloak OpenID client
        try:
            keycloak_openid = KeycloakOpenID(
                server_url = up(issuer).__getattribute__('scheme') + '://' + \
                    up(issuer).__getattribute__('netloc') + '/',
                client_id = client_id,
                realm_name = realm_name,
                client_secret_key = client_secret
            )
        except KeycloakError as err:
            logger. Error(f"An OpenID Client Error happening: ({err.response_code}): {err.response_body}. More details {err.error_message}.")

        # Decode the Plone session cookie
        session_cookie = self.request.cookies.get('__ac')
        decoded_session = base64.b64decode(session_cookie).decode('utf-8')

        # Extract the username and password from the decoded session
        username, password = decoded_session.split(':')
        
        # Get access token with user and password
        token = keycloak_openid.token(username, password)

        # Get UserInfo
        user_info = keycloak_openid.userinfo(token['access_token'])
        logger.info(f"The user_info's from the '{user_info['preferred_username']}' username was got!") 

        return user_info

My Addons extra are:

  • pas.plugins.oidc = 1.0.0
  • python-keycloak = 3.9.1