diff --git a/ansible/roles/keycloak/defaults/main.yml b/ansible/roles/keycloak/defaults/main.yml index 7836d421f75e657b80fafa8d76f940e88ed0333f..637f62a59acff16d05ef43d3472951ca86774526 100644 --- a/ansible/roles/keycloak/defaults/main.yml +++ b/ansible/roles/keycloak/defaults/main.yml @@ -57,11 +57,11 @@ wildfly_standalone_config_path: /opt/keycloak/keycloak/standalone/configuration/ keycloak_ext: <extension module="org.keycloak.keycloak-server-subsystem"/> #keycloak_subsystem: #keycloak_cache_container: -#keycloak_default_ds: +#keycloak_default_ds: keycloak_download_file: keycloak-3.2.0.Final.tar.gz keycloak_ds_driver_url: https://jdbc.postgresql.org/download/postgresql-9.2.1212.jar -#keycloak_ds_driver_path: +#keycloak_ds_driver_path: keycloak_ds_driver_name: "postgresql" keycloak_ds_driver_module: ' <?xml version="1.0" ?> @@ -106,3 +106,10 @@ theme: "sunrise.tar.gz" dest_theme: "/opt/keycloak/themes/" theme_file: sunrise keycloak_home: /opt/keycloak +keycloak_realm_json_file_path: "roles/keycloak/files/python-keycloak-0.12.0/keycloak-realm.json" +keycloak_user_manager_roles_json_file_path: "roles/keycloak/files/python-keycloak-0.12.0/roles.json" +keycloak_api_management_username: "" +keycloak_api_management_user_email: "" +keycloak_api_management_user_first_name: "" +keycloak_api_management_user_last_name: "" +keycloak_api_management_user_password: "" diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/PKG-INFO b/ansible/roles/keycloak/files/python-keycloak-0.12.0/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..e71b54e3b995b675e7f81b254b207e43d625cd6a --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/PKG-INFO @@ -0,0 +1,19 @@ +Metadata-Version: 1.1 +Name: python-keycloak +Version: 0.12.0 +Summary: python-keycloak is a Python package providing access to the Keycloak API. +Home-page: https://bitbucket.org/agriness/python-keycloak +Author: Marcos Pereira +Author-email: marcospereira.mpj@gmail.com +License: GNU General Public License - V3 +Description-Content-Type: UNKNOWN +Description: UNKNOWN +Keywords: keycloak openid +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) +Classifier: Development Status :: 3 - Alpha +Classifier: Operating System :: MacOS +Classifier: Operating System :: Unix +Classifier: Operating System :: Microsoft :: Windows +Classifier: Topic :: Utilities diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/README.md b/ansible/roles/keycloak/files/python-keycloak-0.12.0/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c6b0940b0800fa091d58eaf2ac1b1103ae460878 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/README.md @@ -0,0 +1,210 @@ +[](http://python-keycloak.readthedocs.io/en/latest/?badge=latest) + +Python Keycloak +==================== + +For review- see https://bitbucket.org/agriness/python-keycloak + +**python-keycloak** is a Python package providing access to the Keycloak API. + +## Installation + +### Via Pypi Package: + +``` $ pip install python-keycloak ``` + +### Manually + +``` $ python setup.py install ``` + +## Dependencies + +python-keycloak depends on: + +* Python 3 +* [requests](http://docs.python-requests.org/en/master/) +* [python-jose](http://python-jose.readthedocs.io/en/latest/) + +### Tests Dependencies + +* unittest +* [httmock](https://github.com/patrys/httmock) + +## Bug reports + +Please report bugs and feature requests at +https://bitbucket.org/agriness/python-keycloak/issues + +## Documentation + +The documentation for python-keycloak is available on [readthedocs](http://python-keycloak.readthedocs.io). + +## Contributors + +* [Agriness Team](http://www.agriness.com/pt/) +* [Marcos Pereira](marcospereira.mpj@gmail.com) +* [Martin Devlin](martin.devlin@pearson.com) +* [Shon T. Urbas](shon.urbas@gmail.com>) + +## Usage + +```python +from keycloak import KeycloakOpenID + +# Configure client +keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", + client_id="example_client", + realm_name="example_realm", + client_secret_key="secret") + +# Get WellKnow +config_well_know = keycloak_openid.well_know() + +# Get Token +token = keycloak_openid.token("user", "password") + +# Get Userinfo +userinfo = keycloak_openid.userinfo(token['access_token']) + +# Refresh token +token = keycloak_openid.refresh_token(token['refresh_token']) + +# Logout +keycloak_openid.logout(token['refresh_token']) + +# Get Certs +certs = keycloak_openid.certs() + +# Get RPT (Entitlement) +token = keycloak_openid.token("user", "password") +rpt = keycloak_openid.entitlement(token['access_token'], "resource_id") + +# Instropect RPT +token_rpt_info = keycloak_openid.introspect(keycloak_openid.introspect(token['access_token'], rpt=rpt['rpt'], + token_type_hint="requesting_party_token")) + +# Introspect Token +token_info = keycloak_openid.introspect(token['access_token'])) + +# Decode Token +KEYCLOAK_PUBLIC_KEY = "secret" +options = {"verify_signature": True, "verify_aud": True, "exp": True} +token_info = keycloak_openid.decode_token(token['access_token'], key=KEYCLOAK_PUBLIC_KEY, options=options) + +# Get permissions by token +token = keycloak_openid.token("user", "password") +keycloak_openid.load_authorization_config("example-authz-config.json") +policies = keycloak_openid.get_policies(token['access_token'], method_token_info='decode', key=KEYCLOAK_PUBLIC_KEY) +permissions = keycloak_openid.get_permissions(token['access_token'], method_token_info='introspect') + +# KEYCLOAK ADMIN + +from keycloak import KeycloakAdmin + +keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", + username='example-admin', + password='secret', + realm_name="example_realm", + verify=True) + +# Add user +new_user = keycloak_admin.create_user({"email": "example@example.com", + "username": "example@example.com", + "enabled": True, + "firstName": "Example", + "lastName": "Example", + "realmRoles": ["user_default", ], + "attributes": {"example": "1,2,3,3,"}}) + + +# Add user and set password +new_user = keycloak_admin.create_user({"email": "example@example.com", + "username": "example@example.com", + "enabled": True, + "firstName": "Example", + "lastName": "Example", + "credentials": [{"value": "secret","type": "password",}], + "realmRoles": ["user_default", ], + "attributes": {"example": "1,2,3,3,"}}) + +# User counter +count_users = keycloak_admin.users_count() + +# Get users Returns a list of users, filtered according to query parameters +users = keycloak_admin.get_users({}) + +# Get user ID from name +user-id-keycloak = keycloak_admin.get_user_id("example@example.com") + +# Get User +user = keycloak_admin.get_user("user-id-keycloak") + +# Update User +response = keycloak_admin.update_user(user_id="user-id-keycloak", + payload={'firstName': 'Example Update'}) + +# Update User Password +response = set_user_password(user_id="user-id-keycloak", password="secret", temporary=True) + +# Delete User +response = keycloak_admin.delete_user(user_id="user-id-keycloak") + +# Get consents granted by the user +consents = keycloak_admin.consents_user(user_id="user-id-keycloak") + +# Send User Action +response = keycloak_admin.send_update_account(user_id="user-id-keycloak", + payload=json.dumps(['UPDATE_PASSWORD'])) + +# Send Verify Email +response = keycloak_admin.send_verify_email(user_id="user-id-keycloak") + +# Get sessions associated with the user +sessions = keycloak_admin.get_sessions(user_id="user-id-keycloak") + +# Get themes, social providers, auth providers, and event listeners available on this server +server_info = keycloak_admin.get_server_info() + +# Get clients belonging to the realm Returns a list of clients belonging to the realm +clients = keycloak_admin.get_clients() + +# Get client - id (not client-id) from client by name +client_id=keycloak_admin.get_client_id("my-client") + +# Get representation of the client - id of client (not client-id) +client = keycloak_admin.get_client(client_id="client_id") + +# Get all roles for the realm or client +realm_roles = keycloak_admin.get_realm_roles() + +# Get all roles for the client +client_roles = keycloak_admin.get_client_roles(client_id="client_id") + +# Get client role +role = keycloak_admin.get_client_role(client_id="client_id", role_name="role_name") + +# Warning: Deprecated +# Get client role id from name +role_id = keycloak_admin.get_client_role_id(client_id="client_id", role_name="test") + +# Create client role +keycloak_admin.create_client_role(client_id, "test") + +# Assign client role to user. Note that BOTH role_name and role_id appear to be required. +keycloak_admin.assign_client_role(client_id="client_id", user_id="user_id", role_id="role_id", role_name="test") + +# Create new group +group = keycloak_admin.create_group(name="Example Group") + +# Get all groups +groups = keycloak_admin.get_groups() + +# Get group +group = keycloak_admin.get_group(group_id='group_id') + +# Get group by name +group = keycloak_admin.get_group_by_name(name_or_path='group_id', search_in_subgroups=True) + +# Function to trigger user sync from provider +sync_users(storage_id="storage_di", action="action") +``` diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak-realm.json b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak-realm.json new file mode 100644 index 0000000000000000000000000000000000000000..72416d4de45c06a24bbff5b0e5b29a49116c59f8 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak-realm.json @@ -0,0 +1,1999 @@ +{ + "id": "sunbird", + "realm": "sunbird", + "notBefore": 0, + "revokeRefreshToken": false, + "accessTokenLifespan": 21600, + "accessTokenLifespanForImplicitFlow": 7200, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 21600, + "offlineSessionIdleTimeout": 43200, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "7b955d7f-0a1e-4935-8391-642886d34612", + "name": "offline_access", + "description": "${role_offline-access}", + "scopeParamRequired": true, + "composite": false, + "clientRole": false, + "containerId": "sunbird" + }, + { + "id": "96adf368-c8e2-4b39-b2a5-2559573edb63", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "scopeParamRequired": false, + "composite": false, + "clientRole": false, + "containerId": "sunbird" + } + ], + "client": { + "realm-management": [ + { + "id": "3f8bf7e5-5d66-4394-8f06-1270529c605f", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "601fa2c9-29d4-49c1-87ac-939a1260f6ce", + "name": "query-realms", + "description": "${role_query-realms}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "9aa1dcd9-cb93-4496-af5f-41b9ecacc1da", + "name": "view-authorization", + "description": "${role_view-authorization}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "98999987-faf5-4c4e-958a-e5463bc4edc6", + "name": "manage-events", + "description": "${role_manage-events}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "f898fca8-5361-49d5-900a-ebf5b775a939", + "name": "impersonation", + "description": "${role_impersonation}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "698dfeb0-b8d2-4240-b8a8-acd4b7a12ad3", + "name": "view-realm", + "description": "${role_view-realm}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "3ff462fc-b33c-431a-b54b-861c3298d910", + "name": "manage-users", + "description": "${role_manage-users}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "be1802b1-558c-404c-bcb9-b9bf77af9788", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "387b44e4-e901-4431-b9af-6abd9377ed46", + "name": "query-clients", + "description": "${role_query-clients}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "a408b6e8-03c9-46a2-97ba-305d09db0c3c", + "name": "view-events", + "description": "${role_view-events}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "41c2f39a-3008-4f9d-9e1e-a7738c118570", + "name": "query-groups", + "description": "${role_query-groups}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "26d3289b-d2eb-4cf2-a501-f1e3fa07344c", + "name": "manage-clients", + "description": "${role_manage-clients}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "e2077ab0-6efb-450d-9cba-89cacd887b71", + "name": "create-client", + "description": "${role_create-client}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "46019462-3dc8-46a8-9786-ffcbad293f43", + "name": "view-users", + "description": "${role_view-users}", + "scopeParamRequired": false, + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-groups", + "query-users" + ] + } + }, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "d269f220-e93f-4b43-96a1-9f2c117a2dfb", + "name": "view-clients", + "description": "${role_view-clients}", + "scopeParamRequired": false, + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "0c998f1b-7363-47fb-a493-4b6f4aacb0ba", + "name": "realm-admin", + "description": "${role_realm-admin}", + "scopeParamRequired": false, + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-authorization", + "query-realms", + "view-authorization", + "manage-events", + "impersonation", + "view-realm", + "manage-users", + "manage-identity-providers", + "query-clients", + "view-events", + "query-groups", + "manage-clients", + "create-client", + "view-users", + "view-clients", + "manage-realm", + "view-identity-providers", + "query-users" + ] + } + }, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "d77bf5a5-5877-450b-b11e-5f874f410e10", + "name": "manage-realm", + "description": "${role_manage-realm}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "d97009ed-d0c7-4afb-b9a3-6ee03ef01a74", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "57118202-c5e5-4c49-829b-c2ed796bfdea", + "name": "query-users", + "description": "${role_query-users}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + } + ], + "security-admin-console": [], + "android": [], + "admin-cli": [], + "trampoline": [], + "broker": [ + { + "id": "19ef58ac-2d90-40a4-a158-0e2f8893264a", + "name": "read-token", + "description": "${role_read-token}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "c3950efa-6684-44c2-b50a-c7b3d16df04b" + } + ], + "portal": [], + "account": [ + { + "id": "1fef7ac5-b042-462b-8298-0446044788b3", + "name": "manage-account", + "description": "${role_manage-account}", + "scopeParamRequired": false, + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "c2d24d3f-65ca-46de-9cd8-3eeb71a7f83d" + }, + { + "id": "f8786348-6fa4-4b13-828e-9f080c9c6824", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "c2d24d3f-65ca-46de-9cd8-3eeb71a7f83d" + }, + { + "id": "91c5c738-9c39-4c4d-bae8-75f18fd7c5e4", + "name": "view-profile", + "description": "${role_view-profile}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "c2d24d3f-65ca-46de-9cd8-3eeb71a7f83d" + } + ] + } + }, + "groups": [], + "defaultRoles": [ + "offline_access", + "uma_authorization" + ], + "requiredCredentials": [ + "password" + ], + "passwordPolicy": "hashIterations(20000)", + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "clientScopeMappings": { + "realm-management": [ + { + "client": "admin-cli", + "roles": [ + "realm-admin" + ] + }, + { + "client": "security-admin-console", + "roles": [ + "realm-admin" + ] + } + ] + }, + "clients": [ + { + "id": "9a901d18-377b-4615-9b89-677b544be3c5", + "clientId": "trampoline", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "/", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [ + ], + "webOrigins": [ + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "saml.authnstatement": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "saml.onetimeuse.condition": "false" + }, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "12134027-94cc-401c-bbf6-be565078ddfb", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "6a956bf1-6c40-4549-b335-9fe8c788b18f", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "d32ba1d4-3fef-42ff-aa2b-98cb4bfef6f9", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "9e899fc7-1ddd-447d-810c-d91333d6621c", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "74e4d13f-bd02-4f17-bbbc-d4b79bab1971", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "48fe23c2-a6fb-4c5e-8930-28ad1913829a", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "da893beb-6ac7-420d-b51b-f05dadf56bbc", + "clientId": "android", + "rootUrl": "", + "baseUrl": "/", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [ + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "saml.authnstatement": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "saml.onetimeuse.condition": "false" + }, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "ff7dac46-16b4-4ab6-a054-dd03d5411fa9", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "32fad9c0-0dfe-45b2-94a1-3e2f74d756ec", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "4fb4d644-c609-4a27-a407-d451bcd83e16", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "84e7c6e5-1afb-44d8-b507-1ed31fa0f351", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "e7ca189f-94de-4996-a192-e93ba960bbba", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "16bedb3b-7571-4106-9a47-66151915ac31", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "8891d8e9-35e6-4a1c-b32b-027be03b0f24", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "32d6a3a4-8635-4a36-bd14-8ac5b73a49cc", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "900fa741-d1b2-467e-88a6-b454a9519568", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "8ea14714-d160-49b8-b612-59102d50ef53", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "bd167b99-5d23-49c1-90c7-85c2b088fed7", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "c4fe56a1-8d15-455a-b87b-cf051454b57c", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "26e3bf4c-8f9a-4548-b0db-5bbb7c398991", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "c3950efa-6684-44c2-b50a-c7b3d16df04b", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "54c3bbc3-850b-4636-82d1-ed4f3a46a00a", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "0f0d9b74-c476-4981-a783-dd4bdbe041ec", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "d6f625cc-eac1-49d0-bea5-17e6f9d3860c", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "bbf35056-9bbe-49a0-aefc-2bde2379ccdc", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "38b04951-1043-4dc6-9504-7b0f31ed71a4", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "88bb836b-7feb-45eb-b004-fb3be8436908", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "b2f45201-1362-4b10-83c3-207d470f44bf", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "71605238-bf41-400e-8c03-a5d78f54b00b", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "a0cb6f3a-30ef-4475-b73f-ca6c2f1f3675", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "3a7708b1-63b9-4116-af7f-969fc1bf61a4", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "c393d7b7-fa22-4e90-9ad7-07d520632c20", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "2d8cbac4-e4dd-4fa6-bf71-98adf826b9dd", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "e77aae69-8ea4-4ee4-bcd6-ba7ef3958c02", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "8c12290d-d62f-48ce-913b-c93bf995ca59", + "clientId": "portal", + "rootUrl": "", + "adminUrl": "/callback", + "baseUrl": "/", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [ + ], + "webOrigins": [ + "" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "saml.authnstatement": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "saml.onetimeuse.condition": "false" + }, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "63071ff2-a5e5-4d38-b534-a9f25a075403", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "9bf9cad5-dbce-41e9-aa36-d84cc5a768a2", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "238e55b7-6545-467e-856b-f95477afe1ff", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "edb1ff4e-b452-46bc-8c3b-d6075f6ee579", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "7a0118e2-57ff-4d23-bf74-cbfe1f545d1d", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "b59a913e-118a-4dc4-a8d7-66c44ced5345", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "79c518d7-b41a-4e6f-be42-4ef365824100", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "baseUrl": "/auth/admin/sunbird/console/index.html", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "redirectUris": [ + "/auth/admin/sunbird/console/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "c989a8c8-cb8b-40ff-b4b9-86122bad7aa9", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "b180fb8c-997c-4f6a-b774-af677f903139", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "c373dc9a-49d7-4d28-9b94-06cf20fb1955", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "24c7b1c4-62c2-4d92-ab19-49bfaedcc3d4", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "883a39a7-37b4-46ef-a761-3e51b95ccc35", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "95ed8e44-38cc-4f09-8adc-19c12d5eada0", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "consentText": "${locale}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "c66c6a41-eb22-443f-8a77-e68d404ad26f", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + }, + { + "id": "c2d24d3f-65ca-46de-9cd8-3eeb71a7f83d", + "clientId": "account", + "name": "${client_account}", + "baseUrl": "/auth/realms/sunbird/account", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "", + "defaultRoles": [ + "manage-account", + "view-profile" + ], + "redirectUris": [ + "/auth/realms/sunbird/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "a64118ab-33c8-4060-9f3e-3ed817ba8e0d", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + }, + { + "id": "1f4a0c5e-7c8a-4693-8be5-14681b243868", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${username}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "8d88a133-a399-4e75-b051-5b0d4ae850ab", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": true, + "consentText": "${fullName}", + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "45593258-916c-4158-8577-d9806c16415a", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${email}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "027be48e-b7dc-4c3a-a648-414a466b67dd", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${familyName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "c93937f9-0446-4be9-8b47-3c6de857497e", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": true, + "consentText": "${givenName}", + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + } + ], + "useTemplateConfig": false, + "useTemplateScope": false, + "useTemplateMappers": false + } + ], + "clientTemplates": [], + "browserSecurityHeaders": { + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "xXSSProtection": "1; mode=block", + "contentSecurityPolicy": "frame-src 'self'" + }, + "smtpServer": { + "password": "", + "starttls": "", + "auth": "true", + "port": "587", + "host": "", + "from": "", + "ssl": "", + "user": "" + }, + "loginTheme": "sunrise", + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "84078bbb-e005-44c8-9c7d-a1b4821558da", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "346d857e-4385-4f8f-a2fc-072fd11a10ec", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-usermodel-property-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper" + ], + "consent-required-for-all-mappers": [ + "true" + ] + } + }, + { + "id": "eed64f9f-4b66-45ad-bdb4-4070e3802366", + "name": "Allowed Client Templates", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": {} + }, + { + "id": "17ac4eaa-9139-4b3a-b1db-c82d44c1531d", + "name": "Allowed Client Templates", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "38532936-be91-40e4-b65d-c0abfaf9547c", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "8c1690a2-6eea-4d61-ab66-7a015e3bea3c", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper" + ], + "consent-required-for-all-mappers": [ + "true" + ] + } + }, + { + "id": "3dcc314c-07f8-484d-9535-29424dbaddfc", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "ab52b781-64b9-42a0-99f5-cbeba6710763", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "bd30c46f-9ee3-443d-9faa-6ed8075aac87", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "a93cc73f-b070-48cd-bf08-9b290707c2f5", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": true, + "supportedLocales": [ + "de", + "no", + "ru", + "sv", + "pt-BR", + "ja", + "lt", + "en", + "it", + "fr", + "es", + "ca" + ], + "defaultLocale": "en", + "authenticationFlows": [ + { + "id": "6c9d3423-9956-4d08-a124-41cd17aceb5a", + "alias": "Direct Grant 2", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": false, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "OPTIONAL", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "a7a49eb1-4386-499a-8a8f-13454b428f98", + "alias": "Direct Grant w/o Password", + "description": "Grant user access using only the username and no password.", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": false, + "authenticationExecutions": [] + }, + { + "id": "c7b2f9c2-525b-486a-b2a8-148606caac0e", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "c4674fef-8631-43c5-8f44-deb8867fa866", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "OPTIONAL", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "35016b3a-a041-47fd-8a85-a131c6f7b745", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "8e85927e-349d-4a0a-a7da-efb352ba78ce", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "b62ac783-8af6-429e-a8ff-7c8073195675", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "OPTIONAL", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "e16e69c8-a3e8-480c-8201-8474d614e172", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "487ab3f9-33bf-49e2-8b03-5c45c85ea8b5", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "a6d19ee2-387a-4db8-9ecb-2e9f806a88a1", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "OPTIONAL", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "43fc7877-ad15-4c93-b327-15d4ca83f3e1", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "8a77377f-60eb-4a23-8bfe-b7c360d7b48f", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "30bd3dd1-f2f1-4708-a07d-08cf759c7b28", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "OPTIONAL", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "226a50b1-059a-44ef-8cfe-082dee5d60b9", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "aec72381-3c1d-4a96-b436-ddb72b3737b9", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "51f3fa48-bf74-4df9-9724-c0a9d6fe7c80", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": true, + "defaultAction": false, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "Direct Grant 2", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "_browser_header.xXSSProtection": "1; mode=block", + "_browser_header.xFrameOptions": "SAMEORIGIN", + "permanentLockout": "false", + "quickLoginCheckMilliSeconds": "1000", + "_browser_header.xRobotsTag": "none", + "maxFailureWaitSeconds": "900", + "minimumQuickLoginWaitSeconds": "60", + "failureFactor": "30", + "actionTokenGeneratedByUserLifespan": "300", + "maxDeltaTimeSeconds": "43200", + "_browser_header.xContentTypeOptions": "nosniff", + "actionTokenGeneratedByAdminLifespan": "43200", + "bruteForceProtected": "false", + "_browser_header.contentSecurityPolicy": "frame-src 'self'", + "waitIncrementSeconds": "60" + }, + "keycloakVersion": "3.2.0.Final" +} diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/__init__.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6da124c13c4840a8a1cf814f4266847e6cf54605 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from .keycloak_openid import * +from .keycloak_admin import * +from .keycloak_adminchild import * diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/__init__.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4a1d86dbeba8b6911332d783e5aa62d11795ee7f --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/__init__.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import ast +import json + +from .permission import Permission +from .policy import Policy +from .role import Role + + +class Authorization: + """ + Keycloak Authorization (policies, roles, scopes and resources). + + https://keycloak.gitbooks.io/documentation/authorization_services/index.html + + """ + + def __init__(self): + self._policies = {} + + @property + def policies(self): + return self._policies + + @policies.setter + def policies(self, value): + self._policies = value + + def load_config(self, data): + """ + Load policies, roles and permissions (scope/resources). + + :param data: keycloak authorization data (dict) + :return: + """ + for pol in data['policies']: + if pol['type'] == 'role': + policy = Policy(name=pol['name'], + type=pol['type'], + logic=pol['logic'], + decision_strategy=pol['decisionStrategy']) + + config_roles = json.loads(pol['config']['roles']) + for role in config_roles: + policy.add_role(Role(name=role['id'], + required=role['required'])) + + self.policies[policy.name] = policy + + if pol['type'] == 'scope': + permission = Permission(name=pol['name'], + type=pol['type'], + logic=pol['logic'], + decision_strategy=pol['decisionStrategy']) + + permission.scopes = ast.literal_eval(pol['config']['scopes']) + + for policy_name in ast.literal_eval(pol['config']['applyPolicies']): + self.policies[policy_name].add_permission(permission) + + if pol['type'] == 'resource': + permission = Permission(name=pol['name'], + type=pol['type'], + logic=pol['logic'], + decision_strategy=pol['decisionStrategy']) + + permission.resources = ast.literal_eval(pol['config']['resources']) + + for policy_name in ast.literal_eval(pol['config']['applyPolicies']): + self.policies[policy_name].add_permission(permission) + diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/permission.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/permission.py new file mode 100644 index 0000000000000000000000000000000000000000..94eca779542b9af1639490af19c564db62c07ffd --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/permission.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +class Permission: + """ + Consider this simple and very common permission: + + A permission associates the object being protected with the policies that must be evaluated to determine whether access is granted. + + X CAN DO Y ON RESOURCE Z + + where … + X represents one or more users, roles, or groups, or a combination of them. You can + also use claims and context here. + Y represents an action to be performed, for example, write, view, and so on. + Z represents a protected resource, for example, "/accounts". + + https://keycloak.gitbooks.io/documentation/authorization_services/topics/permission/overview.html + + """ + + def __init__(self, name, type, logic, decision_strategy): + self._name = name + self._type = type + self._logic = logic + self._decision_strategy = decision_strategy + self._resources = [] + self._scopes = [] + + def __repr__(self): + return "<Permission: %s (%s)>" % (self.name, self.type) + + def __str__(self): + return "Permission: %s (%s)" % (self.name, self.type) + + @property + def name(self): + return self._name + + @name.setter + def name(self, value): + self._name = value + + @property + def type(self): + return self._type + + @type.setter + def type(self, value): + self._type = value + + @property + def logic(self): + return self._logic + + @logic.setter + def logic(self, value): + self._logic = value + + @property + def decision_strategy(self): + return self._decision_strategy + + @decision_strategy.setter + def decision_strategy(self, value): + self._decision_strategy = value + + @property + def resources(self): + return self._resources + + @resources.setter + def resources(self, value): + self._resources = value + + @property + def scopes(self): + return self._scopes + + @scopes.setter + def scopes(self, value): + self._scopes = value + diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/policy.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/policy.py new file mode 100644 index 0000000000000000000000000000000000000000..66512bac3a5a0a7efe77f7118f09910535992335 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/policy.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from ..exceptions import KeycloakAuthorizationConfigError + + +class Policy: + """ + A policy defines the conditions that must be satisfied to grant access to an object. + Unlike permissions, you do not specify the object being protected but rather the conditions + that must be satisfied for access to a given object (for example, resource, scope, or both). + Policies are strongly related to the different access control mechanisms (ACMs) that you can use to + protect your resources. With policies, you can implement strategies for attribute-based access control + (ABAC), role-based access control (RBAC), context-based access control, or any combination of these. + + https://keycloak.gitbooks.io/documentation/authorization_services/topics/policy/overview.html + + """ + + def __init__(self, name, type, logic, decision_strategy): + self._name = name + self._type = type + self._logic = logic + self._decision_strategy = decision_strategy + self._roles = [] + self._permissions = [] + + def __repr__(self): + return "<Policy: %s (%s)>" % (self.name, self.type) + + def __str__(self): + return "Policy: %s (%s)" % (self.name, self.type) + + @property + def name(self): + return self._name + + @name.setter + def name(self, value): + self._name = value + + @property + def type(self): + return self._type + + @type.setter + def type(self, value): + self._type = value + + @property + def logic(self): + return self._logic + + @logic.setter + def logic(self, value): + self._logic = value + + @property + def decision_strategy(self): + return self._decision_strategy + + @decision_strategy.setter + def decision_strategy(self, value): + self._decision_strategy = value + + @property + def roles(self): + return self._roles + + @property + def permissions(self): + return self._permissions + + def add_role(self, role): + """ + Add keycloak role in policy. + + :param role: keycloak role. + :return: + """ + if self.type != 'role': + raise KeycloakAuthorizationConfigError( + "Can't add role. Policy type is different of role") + self._roles.append(role) + + def add_permission(self, permission): + """ + Add keycloak permission in policy. + + :param permission: keycloak permission. + :return: + """ + self._permissions.append(permission) diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/role.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/role.py new file mode 100644 index 0000000000000000000000000000000000000000..8d398b09ee0565f2b89e2e899a02c9ec01609cf7 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/authorization/role.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +class Role: + """ + Roles identify a type or category of user. Admin, user, + manager, and employee are all typical roles that may exist in an organization. + + https://keycloak.gitbooks.io/documentation/server_admin/topics/roles.html + + """ + + def __init__(self, name, required=False): + self.name = name + self.required = required + + @property + def get_name(self): + return self.name + + def __eq__(self, other): + if isinstance(other, str): + return self.name == other + return NotImplemented diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/connection.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..503b688a7f11435ece97aee23b0769c651602584 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/connection.py @@ -0,0 +1,199 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +try: + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + +from .exceptions import * +import requests + + +class ConnectionManager(object): + """ Represents a simple server connection. + Args: + base_url (str): The server URL. + headers (dict): The header parameters of the requests to the server. + timeout (int): Timeout to use for requests to the server. + verify (bool): Verify server SSL. + """ + + def __init__(self, base_url, headers={}, timeout=60, verify=True): + self._base_url = base_url + self._headers = headers + self._timeout = timeout + self._verify = verify + + @property + def base_url(self): + """ Return base url in use for requests to the server. """ + return self._base_url + + @base_url.setter + def base_url(self, value): + """ """ + self._base_url = value + + @property + def timeout(self): + """ Return timeout in use for request to the server. """ + return self._timeout + + @timeout.setter + def timeout(self, value): + """ """ + self._timeout = value + + @property + def verify(self): + """ Return verify in use for request to the server. """ + return self._verify + + @verify.setter + def verify(self, value): + """ """ + self._verify = value + + @property + def headers(self): + """ Return header request to the server. """ + return self._headers + + @headers.setter + def headers(self, value): + """ """ + self._headers = value + + def param_headers(self, key): + """ Return a specific header parameter. + :arg + key (str): Header parameters key. + :return: + If the header parameters exist, return its value. + """ + return self.headers.get(key) + + def clean_headers(self): + """ Clear header parameters. """ + self.headers = {} + + def exist_param_headers(self, key): + """ Check if the parameter exists in the header. + :arg + key (str): Header parameters key. + :return: + If the header parameters exist, return True. + """ + return self.param_headers(key) is not None + + def add_param_headers(self, key, value): + """ Add a single parameter inside the header. + :arg + key (str): Header parameters key. + value (str): Value to be added. + """ + self.headers[key] = value + + def del_param_headers(self, key): + """ Remove a specific parameter. + :arg + key (str): Key of the header parameters. + """ + self.headers.pop(key, None) + + def raw_get(self, path, **kwargs): + """ Submit get request to the path. + :arg + path (str): Path for request. + :return + Response the request. + :exception + HttpError: Can't connect to server. + """ + + try: + return requests.get(urljoin(self.base_url, path), + params=kwargs, + headers=self.headers, + timeout=self.timeout, + verify=self.verify) + except Exception as e: + raise KeycloakConnectionError( + "Can't connect to server (%s)" % e) + + def raw_post(self, path, data, **kwargs): + """ Submit post request to the path. + :arg + path (str): Path for request. + data (dict): Payload for request. + :return + Response the request. + :exception + HttpError: Can't connect to server. + """ + try: + return requests.post(urljoin(self.base_url, path), + params=kwargs, + data=data, + headers=self.headers, + timeout=self.timeout, + verify=self.verify) + except Exception as e: + raise KeycloakConnectionError( + "Can't connect to server (%s)" % e) + + def raw_put(self, path, data, **kwargs): + """ Submit put request to the path. + :arg + path (str): Path for request. + data (dict): Payload for request. + :return + Response the request. + :exception + HttpError: Can't connect to server. + """ + try: + return requests.put(urljoin(self.base_url, path), + params=kwargs, + data=data, + headers=self.headers, + timeout=self.timeout, + verify=self.verify) + except Exception as e: + raise KeycloakConnectionError( + "Can't connect to server (%s)" % e) + + def raw_delete(self, path, **kwargs): + """ Submit delete request to the path. + + :arg + path (str): Path for request. + :return + Response the request. + :exception + HttpError: Can't connect to server. + """ + try: + return requests.delete(urljoin(self.base_url, path), + params=kwargs, + headers=self.headers, + timeout=self.timeout, + verify=self.verify) + except Exception as e: + raise KeycloakConnectionError( + "Can't connect to server (%s)" % e) diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/exceptions.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..27d8b14c156086b3de650a4248c4bf09c5cc27c3 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/exceptions.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import requests + + +class KeycloakError(Exception): + def __init__(self, error_message="", response_code=None, + response_body=None): + + Exception.__init__(self, error_message) + + self.response_code = response_code + self.response_body = response_body + self.error_message = error_message + + def __str__(self): + if self.response_code is not None: + return "{0}: {1}".format(self.response_code, self.error_message) + else: + return "{0}".format(self.error_message) + + +class KeycloakAuthenticationError(KeycloakError): + pass + + +class KeycloakConnectionError(KeycloakError): + pass + + +class KeycloakOperationError(KeycloakError): + pass + + +class KeycloakGetError(KeycloakOperationError): + pass + + +class KeycloakSecretNotFound(KeycloakOperationError): + pass + + +class KeycloakRPTNotFound(KeycloakOperationError): + pass + + +class KeycloakAuthorizationConfigError(KeycloakOperationError): + pass + + +class KeycloakInvalidTokenError(KeycloakOperationError): + pass + + +def raise_error_from_response(response, error, expected_code=200): + + if expected_code == response.status_code: + if expected_code == requests.codes.no_content: + return {} + try: + return response.json() + except ValueError: + return response.content + + try: + message = response.json()['message'] + except (KeyError, ValueError): + message = response.content + + if isinstance(error, dict): + error = error.get(response.status_code, KeycloakOperationError) + else: + if response.status_code == 401: + error = KeycloakAuthenticationError + + raise error(error_message=message, + response_code=response.status_code, + response_body=response.content) diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_admin.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_admin.py new file mode 100644 index 0000000000000000000000000000000000000000..79f5c2c6d319fb9cfbe40f8e53ab25a01128e2b6 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_admin.py @@ -0,0 +1,670 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Unless otherwise stated in the comments, "id", in e.g. user_id, refers to the +# internal Keycloak server ID, usually a uuid string +from keycloak.urls_patterns import URL_ADMIN_CLIENT_ROLE +from .urls_patterns import \ + URL_ADMIN_USERS_COUNT, URL_ADMIN_USER, URL_ADMIN_USER_CONSENTS, \ + URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_VERIFY_EMAIL, URL_ADMIN_GET_SESSIONS, \ + URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENTS, URL_ADMIN_CLIENT, URL_ADMIN_CLIENT_ROLES, URL_ADMIN_REALM_ROLES, \ + URL_ADMIN_GROUP, URL_ADMIN_GROUPS, URL_ADMIN_GROUP_CHILD, URL_ADMIN_USER_GROUP,\ + URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_USER_CLIENT_ROLES, URL_ADMIN_USER_STORAGE, URL_ADMIN_REALM + +from .keycloak_openid import KeycloakOpenID + +from .exceptions import raise_error_from_response, KeycloakGetError + +from .urls_patterns import ( + URL_ADMIN_USERS, +) + +from .connection import ConnectionManager +import json + + +class KeycloakAdmin: + + def __init__(self, server_url, username, password, realm_name='master', client_id='admin-cli', verify=True): + """ + + :param server_url: Keycloak server url + :param username: admin username + :param password: admin password + :param realm_name: realm name + :param client_id: client id + :param verify: True if want check connection SSL + """ + self._username = username + self._password = password + self._client_id = client_id + self._realm_name = realm_name + + # Get token Admin + keycloak_openid = KeycloakOpenID(server_url=server_url, client_id=client_id, realm_name=realm_name, + verify=verify) + self._token = keycloak_openid.token(username, password) + + self._connection = ConnectionManager(base_url=server_url, + headers={'Authorization': 'Bearer ' + self.token.get('access_token'), + 'Content-Type': 'application/json'}, + timeout=60, + verify=verify) + + @property + def realm_name(self): + return self._realm_name + + @realm_name.setter + def realm_name(self, value): + self._realm_name = value + + @property + def connection(self): + return self._connection + + @connection.setter + def connection(self, value): + self._connection = value + + @property + def client_id(self): + return self._client_id + + @client_id.setter + def client_id(self, value): + self._client_id = value + + @property + def username(self): + return self._username + + @username.setter + def username(self, value): + self._username = value + + @property + def password(self): + return self._password + + @password.setter + def password(self, value): + self._password = value + + @property + def token(self): + return self._token + + @token.setter + def token(self, value): + self._token = value + + def get_users(self, query=None): + """ + Get users Returns a list of users, filtered according to query parameters + + :return: users list + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path), **query) + return raise_error_from_response(data_raw, KeycloakGetError) + + def create_user(self, payload): + """ + Create a new user Username must be unique + + UserRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation + + :param payload: UserRepresentation + + :return: UserRepresentation + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_post(URL_ADMIN_USERS.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201) + + def users_count(self): + """ + User counter + + :return: counter + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_user_id(self, username): + """ + Get internal keycloak user id from username + This is required for further actions against this user. + + UserRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation + + :param username: id in UserRepresentation + + :return: user_id + """ + params_path = {"realm-name": self.realm_name, "username": username} + data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path)) + data_content = raise_error_from_response(data_raw, KeycloakGetError) + + for user in data_content: + this_use_rname = json.dumps(user["username"]).strip('"') + if this_use_rname == username: + return json.dumps(user["id"]).strip('"') + + return None + + def get_user(self, user_id): + """ + Get representation of the user + + :param user_id: User id + + UserRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation + + :return: UserRepresentation + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.connection.raw_get(URL_ADMIN_USER.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def update_user(self, user_id, payload): + """ + Update the user + + :param user_id: User id + :param payload: UserRepresentation + + :return: Http response + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.connection.raw_put(URL_ADMIN_USER.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def delete_user(self, user_id): + """ + Delete the user + + :param user_id: User id + + :return: Http response + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.connection.raw_delete(URL_ADMIN_USER.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def set_user_password(self, user_id, password, temporary=True): + """ + Set up a password for the user. If temporary is True, the user will have to reset + the temporary password next time they log in. + + http://www.keycloak.org/docs-api/3.2/rest-api/#_users_resource + http://www.keycloak.org/docs-api/3.2/rest-api/#_credentialrepresentation + + :param user_id: User id + :param password: New password + :param temporary: True if password is temporary + + :return: + """ + payload = {"type": "password", "temporary": temporary, "value": password} + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.connection.raw_put(URL_ADMIN_RESET_PASSWORD.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def consents_user(self, user_id): + """ + Get consents granted by the user + + :param user_id: User id + + :return: consents + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.connection.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def send_update_account(self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None): + """ + Send a update account email to the user An email contains a + link the user can click to perform a set of required actions. + + :param user_id: + :param payload: + :param client_id: + :param lifespan: + :param redirect_uri: + + :return: + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri} + data_raw = self.connection.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), + data=payload, **params_query) + return raise_error_from_response(data_raw, KeycloakGetError) + + def send_verify_email(self, user_id, client_id=None, redirect_uri=None): + """ + Send a update account email to the user An email contains a + link the user can click to perform a set of required actions. + + :param user_id: User id + :param client_id: Client id + :param redirect_uri: Redirect uri + + :return: + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + params_query = {"client_id": client_id, "redirect_uri": redirect_uri} + data_raw = self.connection.raw_put(URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), + data={}, **params_query) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_sessions(self, user_id): + """ + Get sessions associated with the user + + :param user_id: id of user + + UserSessionRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation + + :return: UserSessionRepresentation + """ + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.connection.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_server_info(self): + """ + Get themes, social providers, auth providers, and event listeners available on this server + + ServerInfoRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_serverinforepresentation + + :return: ServerInfoRepresentation + """ + data_raw = self.connection.raw_get(URL_ADMIN_SERVER_INFO) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_groups(self): + """ + Get groups belonging to the realm. Returns a list of groups belonging to the realm + + GroupRepresentation + http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation + + :return: array GroupRepresentation + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_ADMIN_GROUPS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_group(self, group_id): + """ + Get group by id. Returns full group details + + GroupRepresentation + http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation + + :return: Keycloak server response (GroupRepresentation) + """ + params_path = {"realm-name": self.realm_name, "id": group_id} + data_raw = self.connection.raw_get(URL_ADMIN_GROUP.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_group_by_name(self, name_or_path, search_in_subgroups=False): + """ + Get group id based on name or path. + A straight name or path match with a top-level group will return first. + Subgroups are traversed, the first to match path (or name with path) is returned. + + GroupRepresentation + http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation + + :param name: group name + :param path: group path + :param search_in_subgroups: True if want search in the subgroups + :return: Keycloak server response (GroupRepresentation) + """ + + groups = self.get_groups() + + # TODO: Review this code is necessary + for group in groups: + if group['name'] == name_or_path or group['path'] == name_or_path: + return group + elif search_in_subgroups and group["subGroups"]: + for subgroup in group["subGroups"]: + if subgroup['name'] == name_or_path or subgroup['path'] == name_or_path: + return subgroup + + return None + + def create_group(self, name=None, client_roles={}, realm_roles=[], sub_groups=[], path=None, parent=None): + """ + Create a group in the Realm + + GroupRepresentation + http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation + + :param name: group name + :param client_roles: (Dict) Client roles to include in groupp # Not demonstrated to work + :param realm_roles: (List) Realm roles to include in group # Not demonstrated to work + :param sub_groups: (List) Subgroups to include in groupp # Not demonstrated to work + :param path: group path + :param parent: parent group's id. Required to create a sub-group. + + :return: Keycloak server response (GroupRepresentation) + """ + + data = {"name": name or path, + "path": path, + "clientRoles": client_roles, + "realmRoles": realm_roles, + "subGroups": sub_groups} + + if parent is None: + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_post(URL_ADMIN_GROUPS.format(**params_path), + data=json.dumps(data)) + else: + params_path = {"realm-name": self.realm_name, "id": parent} + data_raw = self.connection.raw_post(URL_ADMIN_GROUP_CHILD.format(**params_path), + data=json.dumps(data)) + + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201) + + def group_set_permissions(self, group_id, enabled=True): + """ + Enable/Disable permissions for a group. Cannot delete group if disabled + + :param group_id: id of group + :param enabled: boolean + :return: Keycloak server response + """ + + params_path = {"realm-name": self.realm_name, "id": group_id} + data_raw = self.connection.raw_put(URL_ADMIN_GROUP_PERMISSIONS.format(**params_path), + data=json.dumps({"enabled": enabled})) + return raise_error_from_response(data_raw, KeycloakGetError) + + def group_user_add(self, user_id, group_id): + """ + Add user to group (user_id and group_id) + + :param group_id: id of group + :param user_id: id of user + :param group_id: id of group to add to + :return: Keycloak server response + """ + + params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} + data_raw = self.connection.raw_put(URL_ADMIN_USER_GROUP.format(**params_path), data=None) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def group_user_remove(self, user_id, group_id): + """ + Remove user from group (user_id and group_id) + + :param group_id: id of group + :param user_id: id of user + :param group_id: id of group to add to + :return: Keycloak server response + """ + + params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id} + data_raw = self.connection.raw_delete(URL_ADMIN_USER_GROUP.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def delete_group(self, group_id): + """ + Deletes a group in the Realm + + :param group_id: id of group to delete + :return: Keycloak server response + """ + + params_path = {"realm-name": self.realm_name, "id": group_id} + data_raw = self.connection.raw_delete(URL_ADMIN_GROUP.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def get_clients(self): + """ + Get clients belonging to the realm Returns a list of clients belonging to the realm + + ClientRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation + + :return: Keycloak server response (ClientRepresentation) + """ + + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_ADMIN_CLIENTS.format(**params_path)) + + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client(self, client_id): + """ + Get representation of the client + + ClientRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation + + :param client_id: id of client (not client-id) + :return: Keycloak server response (ClientRepresentation) + """ + + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.connection.raw_get(URL_ADMIN_CLIENT.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_id(self, client_name): + """ + Get internal keycloak client id from client-id. + This is required for further actions against this client. + + :param client_name: name in ClientRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation + :return: client_id (uuid as string) + """ + + clients = self.get_clients() + + for client in clients: + if client_name == client['name']: + return client["id"] + + return None + + def create_client(self, payload): + """ + Create a client + + ClientRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation + + :param payload: ClientRepresentation + :return: Keycloak server response (UserRepresentation) + """ + + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_post(URL_ADMIN_CLIENTS.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201) + + def delete_client(self, client_id): + """ + Get representation of the client + + ClientRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation + + :param client_id: keycloak client id (not oauth client-id) + :return: Keycloak server response (ClientRepresentation) + """ + + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.connection.raw_delete(URL_ADMIN_CLIENT.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def get_realm_roles(self): + """ + Get all roles for the realm or client + + RoleRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation + + :return: Keycloak server response (RoleRepresentation) + """ + + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_ADMIN_REALM_ROLES.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_roles(self, client_id): + """ + Get all roles for the client + + :param client_id: id of client (not client-id) + + RoleRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation + + :return: Keycloak server response (RoleRepresentation) + """ + + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_role(self, client_id, role_name): + """ + Get client role id by name + This is required for further actions with this role. + + :param client_id: id of client (not client-id) + :param role_name: role’s name (not id!) + + RoleRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation + + :return: role_id + """ + params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name} + data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ROLE.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def get_client_role_id(self, client_id, role_name): + """ + Warning: Deprecated + + Get client role id by name + This is required for further actions with this role. + + :param client_id: id of client (not client-id) + :param role_name: role’s name (not id!) + + RoleRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation + + :return: role_id + """ + role = self.get_client_role(client_id, role_name) + return role.get("id") + + def create_client_role(self, payload): + """ + Create a client role + + RoleRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation + + :param payload: id of client (not client-id), role_name: name of role + :return: Keycloak server response (RoleRepresentation) + """ + + params_path = {"realm-name": self.realm_name, "id": self.client_id} + data_raw = self.connection.raw_post(URL_ADMIN_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201) + + def delete_client_role(self, role_name): + """ + Create a client role + + RoleRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation + + :param role_name: role’s name (not id!) + """ + params_path = {"realm-name": self.realm_name, "id": self.client_id, "role-name": role_name} + data_raw = self.connection.raw_delete(URL_ADMIN_CLIENT_ROLE.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def assign_client_role(self, user_id, client_id, roles): + """ + Assign a client role to a user + + :param client_id: id of client (not client-id) + :param user_id: id of user + :param client_id: id of client containing role, + :param roles: roles list or role (use RoleRepresentation) + :return Keycloak server response + """ + + payload = roles if isinstance(roles, list) else [roles] + params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id} + data_raw = self.connection.raw_post(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def sync_users(self, storage_id, action): + """ + Function to trigger user sync from provider + + :param storage_id: + :param action: + :return: + """ + data = {'action': action} + params_query = {"action": action} + + params_path = {"realm-name": self.realm_name, "id": storage_id} + data_raw = self.connection.raw_post(URL_ADMIN_USER_STORAGE.format(**params_path), + data=json.dumps(data), **params_query) + return raise_error_from_response(data_raw, KeycloakGetError) + + def import_realm(self, payload): + """ + Imports a realm from a full representation of that realm + + Realmrepresentation + http://www.keycloak.org/docs-api/2.5/rest-api/index.html#_import_a_realm + + :param payload: Realmrepresentation + + :return: Realmrepresentation + """ + data_raw = self.connection.raw_post(URL_ADMIN_REALM, + data = json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201) \ No newline at end of file diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_main.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_main.py new file mode 100644 index 0000000000000000000000000000000000000000..109852154530cabd2d661524406492004430befb --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_main.py @@ -0,0 +1,79 @@ +import json + +from keycloak import KeycloakOpenID +from keycloak import KeycloakAdmin +import urllib2, argparse, json + +# Import realm +def keycloak_import_realm(keycloak_realm_file): + data = json.load(open(keycloak_realm_file)) + realm_import = keycloak_admin.import_realm(data) + +# Add user and set password +def keycloak_create_user(email, username, firstName, lastName, password): + new_user = keycloak_admin.create_user({"email": email, + "username": username, + "emailVerified": True, + "enabled": True, + "firstName": firstName, + "lastName": lastName, + "credentials": [{"value": "12345","type": password}], + "realmRoles": ["user_default"]}) + +# Create the user and assign the role to access the user management API +def update_user_roles(config): + realm_json = json.load(open(config['keycloak_realm_json_file_path'])) + clientId = "realm-management" + + for client in realm_json['clients']: + if clientId == client['clientId']: + client_id = client["id"] + break + + user = keycloak_admin.get_users({"username":config['keycloak_api_management_username']}) + user_id = user[0]['id']; + + # Read the role from file + with open(config['keycloak_user_manager_roles_json_file_path'], 'r') as data_file: + json_data = data_file.read() + + roles = json.loads(json_data) + keycloak_admin.assign_client_role(user_id, client_id, roles) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Configure keycloak user apis') + parser.add_argument('keycloak_bootstrap_config', help='keycloak server url') + args = parser.parse_args() + + with open(args.keycloak_bootstrap_config) as keycloak_bootstrap_config: + config = json.load(keycloak_bootstrap_config) + + try: + # Get access token + keycloak_admin = KeycloakAdmin(server_url=config['keycloak_auth_server_url'], + username=config['keycloak_management_user'], + password=config['keycloak_management_password'], + realm_name="master", + client_id='admin-cli', + verify=False) + # Import realm + keycloak_import_realm(config['keycloak_realm_json_file_path']) + + # Set realm name to sunbird + keycloak_admin.realm_name = config['keycloak_realm'] + + # Add user for user api + keycloak_create_user(email=config['keycloak_api_management_user_email'], + username=config['keycloak_api_management_username'], + firstName=config['keycloak_api_management_user_first_name'], + lastName=config['keycloak_api_management_user_last_name'], + password=config['keycloak_api_management_user_password']) + + # Update user roles for access user management API's + update_user_roles(config) + + except urllib2.HTTPError as e: + error_message = e.read() + print error_message + raise diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_openid.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_openid.py new file mode 100644 index 0000000000000000000000000000000000000000..f1dcde44e32b46848ed5dcc0a1de8a4220e24854 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/keycloak_openid.py @@ -0,0 +1,390 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from .authorization import Authorization +from .exceptions import raise_error_from_response, KeycloakGetError, \ + KeycloakRPTNotFound, KeycloakAuthorizationConfigError, KeycloakInvalidTokenError +from .urls_patterns import ( + URL_TOKEN, + URL_USERINFO, + URL_WELL_KNOWN, + URL_LOGOUT, + URL_CERTS, + URL_ENTITLEMENT, + URL_INTROSPECT +) +from .connection import ConnectionManager +from jose import jwt +import json + + +class KeycloakOpenID: + + def __init__(self, server_url, realm_name, client_id, client_secret_key=None, verify=True): + """ + + :param server_url: Keycloak server url + :param client_id: client id + :param realm_name: realm name + :param client_secret_key: client secret key + :param verify: True if want check connection SSL + """ + self._client_id = client_id + self._client_secret_key = client_secret_key + self._realm_name = realm_name + self._connection = ConnectionManager(base_url=server_url, + headers={}, + timeout=60, + verify=verify) + + self._authorization = Authorization() + + @property + def client_id(self): + return self._client_id + + @client_id.setter + def client_id(self, value): + self._client_id = value + + @property + def client_secret_key(self): + return self._client_secret_key + + @client_secret_key.setter + def client_secret_key(self, value): + self._client_secret_key = value + + @property + def realm_name(self): + return self._realm_name + + @realm_name.setter + def realm_name(self, value): + self._realm_name = value + + @property + def connection(self): + return self._connection + + @connection.setter + def connection(self, value): + self._connection = value + + @property + def authorization(self): + return self._authorization + + @authorization.setter + def authorization(self, value): + self._authorization = value + + def _add_secret_key(self, payload): + """ + Add secret key if exist. + + :param payload: + :return: + """ + if self.client_secret_key: + payload.update({"client_secret": self.client_secret_key}) + + return payload + + def _build_name_role(self, role): + """ + + :param role: + :return: + """ + return self.client_id + "/" + role + + def _token_info(self, token, method_token_info, **kwargs): + """ + + :param token: + :param method_token_info: + :param kwargs: + :return: + """ + if method_token_info == 'introspect': + token_info = self.introspect(token) + else: + token_info = self.decode_token(token, **kwargs) + + return token_info + + def well_know(self): + """ The most important endpoint to understand is the well-known configuration + endpoint. It lists endpoints and other configuration options relevant to + the OpenID Connect implementation in Keycloak. + + :return It lists endpoints and other configuration options relevant. + """ + + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_WELL_KNOWN.format(**params_path)) + + return raise_error_from_response(data_raw, KeycloakGetError) + + def auth_url(self, redirect_uri): + """ + + http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint + + :return: + """ + return NotImplemented + + def token(self, username, password, grant_type=["password"]): + """ + The token endpoint is used to obtain tokens. Tokens can either be obtained by + exchanging an authorization code or by supplying credentials directly depending on + what flow is used. The token endpoint is also used to obtain new access tokens + when they expire. + + http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint + + :param username: + :param password: + :param grant_type: + :return: + """ + params_path = {"realm-name": self.realm_name} + payload = {"username": username, "password": password, + "client_id": self.client_id, "grant_type": grant_type} + + payload = self._add_secret_key(payload) + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), + data=payload) + return raise_error_from_response(data_raw, KeycloakGetError) + + def refresh_token(self, refresh_token, grant_type=["refresh_token"]): + """ + The token endpoint is used to obtain tokens. Tokens can either be obtained by + exchanging an authorization code or by supplying credentials directly depending on + what flow is used. The token endpoint is also used to obtain new access tokens + when they expire. + + http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint + + :param refresh_token: + :param grant_type: + :return: + """ + params_path = {"realm-name": self.realm_name} + payload = {"client_id": self.client_id, "grant_type": grant_type, "refresh_token": refresh_token} + payload = self._add_secret_key(payload) + data_raw = self.connection.raw_post(URL_TOKEN.format(**params_path), + data=payload) + return raise_error_from_response(data_raw, KeycloakGetError) + + def userinfo(self, token): + """ + The userinfo endpoint returns standard claims about the authenticated user, + and is protected by a bearer token. + + http://openid.net/specs/openid-connect-core-1_0.html#UserInfo + + :param token: + :return: + """ + + self.connection.add_param_headers("Authorization", "Bearer " + token) + params_path = {"realm-name": self.realm_name} + + data_raw = self.connection.raw_get(URL_USERINFO.format(**params_path)) + + return raise_error_from_response(data_raw, KeycloakGetError) + + def logout(self, refresh_token): + """ + The logout endpoint logs out the authenticated user. + :param refresh_token: + :return: + """ + params_path = {"realm-name": self.realm_name} + payload = {"client_id": self.client_id, "refresh_token": refresh_token} + + payload = self._add_secret_key(payload) + data_raw = self.connection.raw_post(URL_LOGOUT.format(**params_path), + data=payload) + + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def certs(self): + """ + The certificate endpoint returns the public keys enabled by the realm, encoded as a + JSON Web Key (JWK). Depending on the realm settings there can be one or more keys enabled + for verifying tokens. + + https://tools.ietf.org/html/rfc7517 + + :return: + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.connection.raw_get(URL_CERTS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def entitlement(self, token, resource_server_id): + """ + Client applications can use a specific endpoint to obtain a special security token + called a requesting party token (RPT). This token consists of all the entitlements + (or permissions) for a user as a result of the evaluation of the permissions and authorization + policies associated with the resources being requested. With an RPT, client applications can + gain access to protected resources at the resource server. + + :return: + """ + self.connection.add_param_headers("Authorization", "Bearer " + token) + params_path = {"realm-name": self.realm_name, "resource-server-id": resource_server_id} + data_raw = self.connection.raw_get(URL_ENTITLEMENT.format(**params_path)) + + return raise_error_from_response(data_raw, KeycloakGetError) + + def introspect(self, token, rpt=None, token_type_hint=None): + """ + The introspection endpoint is used to retrieve the active state of a token. It is can only be + invoked by confidential clients. + + https://tools.ietf.org/html/rfc7662 + + :param token: + :param rpt: + :param token_type_hint: + + :return: + """ + params_path = {"realm-name": self.realm_name} + + payload = {"client_id": self.client_id, "token": token} + + if token_type_hint == 'requesting_party_token': + if rpt: + payload.update({"token": rpt, "token_type_hint": token_type_hint}) + self.connection.add_param_headers("Authorization", "Bearer " + token) + else: + raise KeycloakRPTNotFound("Can't found RPT.") + + payload = self._add_secret_key(payload) + + data_raw = self.connection.raw_post(URL_INTROSPECT.format(**params_path), + data=payload) + + return raise_error_from_response(data_raw, KeycloakGetError) + + def decode_token(self, token, key, algorithms=['RS256'], **kwargs): + """ + A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data + structure that represents a cryptographic key. This specification + also defines a JWK Set JSON data structure that represents a set of + JWKs. Cryptographic algorithms and identifiers for use with this + specification are described in the separate JSON Web Algorithms (JWA) + specification and IANA registries established by that specification. + + https://tools.ietf.org/html/rfc7517 + + :param token: + :param key: + :param algorithms: + :return: + """ + + return jwt.decode(token, key, algorithms=algorithms, + audience=self.client_id, **kwargs) + + def load_authorization_config(self, path): + """ + Load Keycloak settings (authorization) + + :param path: settings file (json) + :return: + """ + authorization_file = open(path, 'r') + authorization_json = json.loads(authorization_file.read()) + self.authorization.load_config(authorization_json) + authorization_file.close() + + def get_policies(self, token, method_token_info='introspect', **kwargs): + """ + Get policies by user token + + :param token: user token + :return: policies list + """ + + if not self.authorization.policies: + raise KeycloakAuthorizationConfigError( + "Keycloak settings not found. Load Authorization Keycloak settings." + ) + + token_info = self._token_info(token, method_token_info, **kwargs) + + if method_token_info == 'introspect' and not token_info['active']: + raise KeycloakInvalidTokenError( + "Token expired or invalid." + ) + + user_resources = token_info['resource_access'].get(self.client_id) + + if not user_resources: + return None + + policies = [] + + for policy_name, policy in self.authorization.policies.items(): + for role in user_resources['roles']: + if self._build_name_role(role) in policy.roles: + policies.append(policy) + + return list(set(policies)) + + def get_permissions(self, token, method_token_info='introspect', **kwargs): + """ + Get permission by user token + + :param token: user token + :param method_token_info: Decode token method + :param kwargs: parameters for decode + :return: permissions list + """ + + if not self.authorization.policies: + raise KeycloakAuthorizationConfigError( + "Keycloak settings not found. Load Authorization Keycloak settings." + ) + + token_info = self._token_info(token, method_token_info, **kwargs) + + if method_token_info == 'introspect' and not token_info['active']: + raise KeycloakInvalidTokenError( + "Token expired or invalid." + ) + + user_resources = token_info['resource_access'].get(self.client_id) + + if not user_resources: + return None + + permissions = [] + + for policy_name, policy in self.authorization.policies.items(): + for role in user_resources['roles']: + if self._build_name_role(role) in policy.roles: + permissions += policy.permissions + + return list(set(permissions)) diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/PKG-INFO b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..e98a1ee3c0a656997e8a87a4d92881f1c17cd0f3 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.1 +Name: python-keycloak +Version: 0.12.0 +Summary: python-keycloak is a Python package providing access to the Keycloak API. +Home-page: https://bitbucket.org/agriness/python-keycloak +Author: Marcos Pereira +Author-email: marcospereira.mpj@gmail.com +License: GNU General Public License - V3 +Description: UNKNOWN +Keywords: keycloak openid +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) +Classifier: Development Status :: 3 - Alpha +Classifier: Operating System :: MacOS +Classifier: Operating System :: Unix +Classifier: Operating System :: Microsoft :: Windows +Classifier: Topic :: Utilities diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/SOURCES.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/dependency_links.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/requires.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/requires.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6eafdf65702ff256e75b81a0f7fbdbcc975e899 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/requires.txt @@ -0,0 +1,3 @@ +requests==2.18.4 +httmock==1.2.5 +python-jose==1.4.0 diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/top_level.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..26c07c1da4a50b7acffbd55aa039a9d4e0d27494 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/python_keycloak.egg-info/top_level.txt @@ -0,0 +1 @@ +keycloak diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/tests/__init__.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/tests/test_connection.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/tests/test_connection.py new file mode 100644 index 0000000000000000000000000000000000000000..97ec1792dfa5312c4b6c5d2fa4563e7577a3037a --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/tests/test_connection.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from httmock import urlmatch, response, HTTMock, all_requests + +from ..connection import ConnectionManager + + +try: + import unittest +except ImportError: + import unittest2 as unittest + + +class TestConnection(unittest.TestCase): + + def setUp(self): + self._conn = ConnectionManager( + base_url="http://localhost:8080/", + headers={}, + timeout=60) + + @all_requests + def response_content_success(self, url, request): + headers = {'content-type': 'application/json'} + content = b'response_ok' + return response(200, content, headers, None, 5, request) + + def test_raw_get(self): + with HTTMock(self.response_content_success): + resp = self._conn.raw_get("/known_path") + self.assertEqual(resp.content, b'response_ok') + self.assertEqual(resp.status_code, 200) + + def test_raw_post(self): + + @urlmatch(path="/known_path", method="post") + def response_post_success(url, request): + headers = {'content-type': 'application/json'} + content = 'response'.encode("utf-8") + return response(201, content, headers, None, 5, request) + + with HTTMock(response_post_success): + resp = self._conn.raw_post("/known_path", + {'field': 'value'}) + self.assertEqual(resp.content, b'response') + self.assertEqual(resp.status_code, 201) + + def test_raw_put(self): + @urlmatch(netloc="localhost", path="/known_path", method="put") + def response_put_success(url, request): + headers = {'content-type': 'application/json'} + content = 'response'.encode("utf-8") + return response(200, content, headers, None, 5, request) + + with HTTMock(response_put_success): + resp = self._conn.raw_put("/known_path", + {'field': 'value'}) + self.assertEqual(resp.content, b'response') + self.assertEqual(resp.status_code, 200) + + def test_raw_get_fail(self): + + @urlmatch(netloc="localhost", path="/known_path", method="get") + def response_get_fail(url, request): + headers = {'content-type': 'application/json'} + content = "404 page not found".encode("utf-8") + return response(404, content, headers, None, 5, request) + + with HTTMock(response_get_fail): + resp = self._conn.raw_get("/known_path") + + self.assertEqual(resp.content, b"404 page not found") + self.assertEqual(resp.status_code, 404) + + def test_raw_post_fail(self): + + @urlmatch(netloc="localhost", path="/known_path", method="post") + def response_post_fail(url, request): + headers = {'content-type': 'application/json'} + content = str(["Start can't be blank"]).encode("utf-8") + return response(404, content, headers, None, 5, request) + + with HTTMock(response_post_fail): + resp = self._conn.raw_post("/known_path", + {'field': 'value'}) + self.assertEqual(resp.content, str(["Start can't be blank"]).encode("utf-8")) + self.assertEqual(resp.status_code, 404) + + def test_raw_put_fail(self): + + @urlmatch(netloc="localhost", path="/known_path", method="put") + def response_put_fail(url, request): + headers = {'content-type': 'application/json'} + content = str(["Start can't be blank"]).encode("utf-8") + return response(404, content, headers, None, 5, request) + + with HTTMock(response_put_fail): + resp = self._conn.raw_put("/known_path", + {'field': 'value'}) + self.assertEqual(resp.content, str(["Start can't be blank"]).encode("utf-8")) + self.assertEqual(resp.status_code, 404) + + def test_add_param_headers(self): + self._conn.add_param_headers("test", "value") + self.assertEqual(self._conn.headers, + {"test": "value"}) + + def test_del_param_headers(self): + self._conn.add_param_headers("test", "value") + self._conn.del_param_headers("test") + self.assertEqual(self._conn.headers, {}) + + def test_clean_param_headers(self): + self._conn.add_param_headers("test", "value") + self.assertEqual(self._conn.headers, + {"test": "value"}) + self._conn.clean_headers() + self.assertEqual(self._conn.headers, {}) + + def test_exist_param_headers(self): + self._conn.add_param_headers("test", "value") + self.assertTrue(self._conn.exist_param_headers("test")) + self.assertFalse(self._conn.exist_param_headers("test_no")) + + def test_get_param_headers(self): + self._conn.add_param_headers("test", "value") + self.assertTrue(self._conn.exist_param_headers("test")) + self.assertFalse(self._conn.exist_param_headers("test_no")) + + def test_get_headers(self): + self._conn.add_param_headers("test", "value") + self.assertEqual(self._conn.headers, + {"test": "value"}) diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/urls_patterns.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/urls_patterns.py new file mode 100644 index 0000000000000000000000000000000000000000..b57212950b4b22de76174803be0969ca78e79ba1 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/keycloak/urls_patterns.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# OPENID URLS +URL_WELL_KNOWN = "realms/{realm-name}/.well-known/openid-configuration" +URL_TOKEN = "realms/{realm-name}/protocol/openid-connect/token" +URL_USERINFO = "realms/{realm-name}/protocol/openid-connect/userinfo" +URL_LOGOUT = "realms/{realm-name}/protocol/openid-connect/logout" +URL_CERTS = "realms/{realm-name}/protocol/openid-connect/certs" +URL_INTROSPECT = "realms/{realm-name}/protocol/openid-connect/token/introspect" +URL_ENTITLEMENT = "realms/{realm-name}/authz/entitlement/{resource-server-id}" + +# ADMIN URLS +URL_ADMIN_USERS = "admin/realms/{realm-name}/users" +URL_ADMIN_USERS_COUNT = "admin/realms/{realm-name}/users/count" +URL_ADMIN_USER = "admin/realms/{realm-name}/users/{id}" +URL_ADMIN_USER_CONSENTS = "admin/realms/{realm-name}/users/{id}/consents" +URL_ADMIN_SEND_UPDATE_ACCOUNT = "admin/realms/{realm-name}/users/{id}/execute-actions-email" +URL_ADMIN_SEND_VERIFY_EMAIL = "admin/realms/{realm-name}/users/{id}/send-verify-email" +URL_ADMIN_RESET_PASSWORD = "admin/realms/{realm-name}/users/{id}/reset-password" +URL_ADMIN_GET_SESSIONS = "admin/realms/{realm-name}/users/{id}/sessions" +URL_ADMIN_USER_CLIENT_ROLES = "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}" +URL_ADMIN_USER_GROUP = "admin/realms/{realm-name}/users/{id}/groups/{group-id}" + +URL_ADMIN_SERVER_INFO = "admin/serverinfo" + +URL_ADMIN_GROUPS = "admin/realms/{realm-name}/groups" +URL_ADMIN_GROUP = "admin/realms/{realm-name}/groups/{id}" +URL_ADMIN_GROUP_CHILD = "admin/realms/{realm-name}/groups/{id}/children" +URL_ADMIN_GROUP_PERMISSIONS = "admin/realms/{realm-name}/groups/{id}/management/permissions" + +URL_ADMIN_CLIENTS = "admin/realms/{realm-name}/clients" +URL_ADMIN_CLIENT = "admin/realms/{realm-name}/clients/{id}" +URL_ADMIN_CLIENT_ROLES = "admin/realms/{realm-name}/clients/{id}/roles" +URL_ADMIN_CLIENT_ROLE = "admin/realms/{realm-name}/clients/{id}/roles/{role-name}" + +URL_ADMIN_REALM_ROLES = "admin/realms/{realm-name}/roles" + +URL_ADMIN_USER_STORAGE = "admin/realms/{realm-name}/user-storage/{id}/sync" +URL_ADMIN_REALM = "admin/realms" diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/PKG-INFO b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..e98a1ee3c0a656997e8a87a4d92881f1c17cd0f3 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.1 +Name: python-keycloak +Version: 0.12.0 +Summary: python-keycloak is a Python package providing access to the Keycloak API. +Home-page: https://bitbucket.org/agriness/python-keycloak +Author: Marcos Pereira +Author-email: marcospereira.mpj@gmail.com +License: GNU General Public License - V3 +Description: UNKNOWN +Keywords: keycloak openid +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) +Classifier: Development Status :: 3 - Alpha +Classifier: Operating System :: MacOS +Classifier: Operating System :: Unix +Classifier: Operating System :: Microsoft :: Windows +Classifier: Topic :: Utilities diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/SOURCES.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..d0e7305a6e0d611c5de47f23bca28423fabd2742 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/SOURCES.txt @@ -0,0 +1,22 @@ +README.md +setup.cfg +setup.py +keycloak/__init__.py +keycloak/connection.py +keycloak/exceptions.py +keycloak/keycloak_admin.py +keycloak/keycloak_adminchild.py +keycloak/keycloak_main.py +keycloak/keycloak_openid.py +keycloak/urls_patterns.py +keycloak/authorization/__init__.py +keycloak/authorization/permission.py +keycloak/authorization/policy.py +keycloak/authorization/role.py +keycloak/tests/__init__.py +keycloak/tests/test_connection.py +python_keycloak.egg-info/PKG-INFO +python_keycloak.egg-info/SOURCES.txt +python_keycloak.egg-info/dependency_links.txt +python_keycloak.egg-info/requires.txt +python_keycloak.egg-info/top_level.txt \ No newline at end of file diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/dependency_links.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/requires.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/requires.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6eafdf65702ff256e75b81a0f7fbdbcc975e899 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/requires.txt @@ -0,0 +1,3 @@ +requests==2.18.4 +httmock==1.2.5 +python-jose==1.4.0 diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/top_level.txt b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..26c07c1da4a50b7acffbd55aa039a9d4e0d27494 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/python_keycloak.egg-info/top_level.txt @@ -0,0 +1 @@ +keycloak diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/roles.json b/ansible/roles/keycloak/files/python-keycloak-0.12.0/roles.json new file mode 100644 index 0000000000000000000000000000000000000000..9aba580d5292e4126a40c321b47f42e7741318d1 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/roles.json @@ -0,0 +1,28 @@ +[ + { + "id": "3ff462fc-b33c-431a-b54b-861c3298d910", + "name": "manage-users", + "description": "${role_manage-users}", + "scopeParamRequired": false, + "composite": false,"clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id": "57118202-c5e5-4c49-829b-c2ed796bfdea", + "name": "query-users", + "description": "${role_query-users}", + "scopeParamRequired": false, + "composite": false, + "clientRole": true, + "containerId": "b2f45201-1362-4b10-83c3-207d470f44bf" + }, + { + "id":"46019462-3dc8-46a8-9786-ffcbad293f43", + "name":"view-users", + "description":"${role_view-users}", + "scopeParamRequired":false, + "composite":true, + "clientRole":true, + "containerId":"b2f45201-1362-4b10-83c3-207d470f44bf" + } +] \ No newline at end of file diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/setup.cfg b/ansible/roles/keycloak/files/python-keycloak-0.12.0/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..9f88734b50dc5105d44ecd76494e065e8437e14f --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/setup.cfg @@ -0,0 +1,7 @@ +[metadata] +description-file = README.md + +[egg_info] +tag_build = +tag_date = 0 + diff --git a/ansible/roles/keycloak/files/python-keycloak-0.12.0/setup.py b/ansible/roles/keycloak/files/python-keycloak-0.12.0/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..bf6a505f0a0816299d694a5e6db9ecca8421b449 --- /dev/null +++ b/ansible/roles/keycloak/files/python-keycloak-0.12.0/setup.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +from setuptools import setup + +setup( + name='python-keycloak', + version='0.12.0', + url='https://bitbucket.org/agriness/python-keycloak', + license='GNU General Public License - V3', + author='Marcos Pereira', + author_email='marcospereira.mpj@gmail.com', + keywords='keycloak openid', + description=u'python-keycloak is a Python package providing access to the Keycloak API.', + packages=['keycloak', 'keycloak.authorization', 'keycloak.tests'], + install_requires=['requests==2.18.4', 'httmock==1.2.5', 'python-jose==1.4.0'], + classifiers=[ + 'Programming Language :: Python :: 3', + 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', + 'Development Status :: 3 - Alpha', + 'Operating System :: MacOS', + 'Operating System :: Unix', + 'Operating System :: Microsoft :: Windows', + 'Topic :: Utilities' + ] +) diff --git a/ansible/roles/keycloak/tasks/keycloak_bootstrap.yml b/ansible/roles/keycloak/tasks/keycloak_bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..6b4807a15a4cc3a9d0d695a4f4d61c21700215ca --- /dev/null +++ b/ansible/roles/keycloak/tasks/keycloak_bootstrap.yml @@ -0,0 +1,8 @@ +- name: initialize python library to run keycloak bootstrap script + shell: python roles/keycloak/files/python-keycloak-0.12.0/keycloak/setup.py install + +- name: Save keycalok vars to json + template: src="roles/keycloak/templates/keycloak-bootstrap.conf.j2" dest="/tmp/keycloak-bootstrap.conf.json" mode="0644" + +- name: Run the keycloak bootstrap script + shell: python keycloak_main.py /tmp/keycloak-bootstrap.conf.json \ No newline at end of file diff --git a/ansible/roles/keycloak/tasks/main.yml b/ansible/roles/keycloak/tasks/main.yml index 0965ef98af028c8f08b0134ec156e14b66aac15d..23f4fa229f9b81fabec19fb1a79ad25fbabb501c 100644 --- a/ansible/roles/keycloak/tasks/main.yml +++ b/ansible/roles/keycloak/tasks/main.yml @@ -6,3 +6,6 @@ tags: - deploy +- include: keycloak_bootstrap.yml + tags: + - keycloak-bootstrap.yml \ No newline at end of file diff --git a/ansible/roles/keycloak/templates/keycloak-bootstrap.conf.j2 b/ansible/roles/keycloak/templates/keycloak-bootstrap.conf.j2 new file mode 100644 index 0000000000000000000000000000000000000000..e17a8594c16be86a8d97314f700bc4d050c3dab4 --- /dev/null +++ b/ansible/roles/keycloak/templates/keycloak-bootstrap.conf.j2 @@ -0,0 +1,13 @@ +{ + "keycloak_auth_server_url": "{{ keycloak_auth_server_url }}", + "keycloak_management_user": "{{ keycloak_management_user }}", + "keycloak_management_password": "{{ keycloak_management_password }}", + "keycloak_realm": "{{ keycloak_realm }}", + "keycloak_realm_json_file_path": "{{keycloak_realm_json_file_path}}", + "keycloak_user_manager_roles_json_file_path": "{{ keycloak_user_manager_roles_json_file_path }}", + "keycloak_api_management_username": "{{ keycloak_api_management_username }}", + "keycloak_api_management_user_email": "{{ keycloak_api_management_user_email }}", + "keycloak_api_management_user_first_name": "{{ keycloak_api_management_user_first_name }}", + "keycloak_api_management_user_last_name": "{{ keycloak_api_management_user_last_name }}", + "keycloak_api_management_user_password": "{{ keycloak_api_management_user_password }}" +}