diff --git a/deploy/oauth/README.md b/deploy/oauth/README.md new file mode 100644 index 000000000..6c824b292 --- /dev/null +++ b/deploy/oauth/README.md @@ -0,0 +1,43 @@ +## Oauth2-Proxy + +Used to locally, manually test te behaviour of auth-reverse-proxies like ALB. + +Based on the suggested docker images from the main [oauth2-proxy environment](https://github.com/oauth2-proxy/oauth2-proxy/tree/master/contrib/local-environment) + +### Pre-requisites + +Add this to your /etc/hosts (uncommented) + +``` +127.0.0.1 keycloak +127.0.0.1 oauth2-proxy +``` + +Credentials | admin/password +Email | admin@datavisyn.io + +# Configuration + +Frontend env (common env in dev, different in prod) via webpack dotenv + +## LOGOUT_URL + +The logout url consists of multiple redirects. First we need to perform the oauth2 proxy sign out via calling `/oauth2/sign_out`. +This sign out procedure deletes the oauth2 cookie and redirects to the open id connect provider via `rd=...`. The redirect url must be an encoded url. +In our local test case, the redirect url will then point to keycloak's openid-connect logout: `auth/realms/{realm}/protocol/openid-connect/logout`. This ensures that the oidc provider also knows that the user should be logged out to prevent automatic redirects to the app with a valid cookie. + +``` +?rd=http://keycloak:9080/auth/realms/master/protocol/openid-connect/logout?redirect_uri=http://localhost:4180 +``` + +- REFRESH_URL + +Backend env + +```env +VISYN_CORE__SECURITY__STORE__OAUTH2_SECURITY_STORE__ENABLE=true +VISYN_CORE__SECURITY__STORE__OAUTH2_SECURITY_STORE__ACCESS_TOKEN_HEADER_NAME=x-forwarded-access-token +VISYN_CORE__SECURITY__STORE__OAUTH2_SECURITY_STORE__COOKIE_NAME=_oauth2_proxy +VISYN_CORE__SECURITY__STORE__OAUTH2_SECURITY_STORE__SIGNOUT_URL=http://localhost:4180/oauth2/sign_out?rd=http%3A%2F%2Fkeycloak%3A9080%2Fauth%2Frealms%2Fmaster%2Fprotocol%2Fopenid-connect%2Flogout%3Fredirect_uri%3Dhttp%3A%2F%2Flocalhost%3A4180 + +``` diff --git a/deploy/oauth/docker-compose.yaml b/deploy/oauth/docker-compose.yaml new file mode 100644 index 000000000..bc89472e9 --- /dev/null +++ b/deploy/oauth/docker-compose.yaml @@ -0,0 +1,39 @@ +# Simplified variant of https://github.com/oauth2-proxy/oauth2-proxy/blob/master/contrib/local-environment/docker-compose-keycloak.yaml +version: '3.0' +services: + oauth2-proxy: + container_name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.3.0 + command: --config /oauth2-proxy.cfg + hostname: oauth2-proxy + volumes: + - './oauth2-proxy.cfg:/oauth2-proxy.cfg' + restart: unless-stopped + depends_on: + - keycloak + network_mode: host + # required for oauth2-proxy.cfg - upstreams localhost, as the frontend isn't available inside docker + + keycloak: + container_name: keycloak + image: jboss/keycloak:10.0.0 + hostname: keycloak + command: + [ + '-Djboss.socket.binding.port-offset=1000', + '-Dkeycloak.migration.action=import', + '-Dkeycloak.migration.provider=dir', + '-Dkeycloak.migration.dir=/realm-config', + '-Dkeycloak.migration.strategy=IGNORE_EXISTING', + ] + volumes: + - ./keycloak:/realm-config + environment: + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: password + ports: + - 9080:9080/tcp +# this docker image REALLY doesn't like to run in network-mode host +# requires this in your /etc/hosts (uncommented) +# 127.0.0.1 keycloak +# 127.0.0.1 oauth2-proxy diff --git a/deploy/oauth/keycloak/master-realm.json b/deploy/oauth/keycloak/master-realm.json new file mode 100644 index 000000000..7c1b3d986 --- /dev/null +++ b/deploy/oauth/keycloak/master-realm.json @@ -0,0 +1,1686 @@ +{ + "id" : "master", + "realm" : "master", + "displayName" : "Keycloak", + "displayNameHtml" : "
Keycloak
", + "notBefore" : 0, + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 36000, + "accessTokenLifespanForImplicitFlow" : 36000, + "ssoSessionIdleTimeout" : 36000, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "accessCodeLifespan" : 36000, + "accessCodeLifespanUserAction" : 36000, + "accessCodeLifespanLogin" : 36000, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 36000, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "32626c92-4327-40f1-b318-76a6b5c7eee5", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "master", + "attributes" : { } + }, { + "id" : "e36da570-7ae0-4323-8b39-73eb92ce722f", + "name" : "admin", + "description" : "${role_admin}", + "composite" : true, + "composites" : { + "realm" : [ "create-realm" ], + "client" : { + "master-realm" : [ "query-groups", "create-client", "query-realms", "view-authorization", "view-realm", "manage-clients", "query-users", "manage-realm", "view-events", "manage-events", "view-identity-providers", "view-users", "manage-identity-providers", "manage-authorization", "manage-users", "view-clients", "query-clients", "impersonation" ] + } + }, + "clientRole" : false, + "containerId" : "master", + "attributes" : { } + }, { + "id" : "71aca46c-6fcf-4456-ba87-6374e70108a2", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "master", + "attributes" : { } + }, { + "id" : "6ca3fee8-1a3f-4068-a311-6e81223a884b", + "name" : "create-realm", + "description" : "${role_create-realm}", + "composite" : false, + "clientRole" : false, + "containerId" : "master", + "attributes" : { } + } ], + "client" : { + "oauth2-proxy" : [ ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "2cc5e40c-0a28-4c09-85eb-20cd47ac1351", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "380985f1-61c7-4940-93ae-7a09458071ca", + "attributes" : { } + } ], + "master-realm" : [ { + "id" : "a8271c2c-6437-4ca5-ae83-49ea5fe1318d", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "5a7cb1ae-7dac-486b-bf7b-4d7fbc5adb31", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "a9e6a2fa-c31b-4959-bf8a-a46fcc9c65ec", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "1cef34e3-569a-4d2b-ba5c-aafe5c7ab423", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "efc46075-30cd-4600-aa92-2ae4a171d0c2", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "9ffacaf0-afc6-49e9-8708-ef35ac40f3f8", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "90662091-b3bc-4ae4-83c9-a4f53e7e9eeb", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "9a5fbc9d-6fae-4155-86f6-72fd399aa126", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "03f46127-9436-477d-8c7f-58569f45237c", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "f10eaea2-90ab-4310-9d5f-8d986564d061", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "2403e038-2cf7-4b06-b5cb-33a417a00d8d", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "677d057b-66f8-4163-9948-95fdbd06dfdc", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "master-realm" : [ "query-groups", "query-users" ] + } + }, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "dc140fa6-bf2c-49f2-b8c9-fc34ef8a2c63", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "155bf234-4895-4855-95c2-a460518f57e8", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "5441ec71-3eac-4696-9e68-0de54fbdde98", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "2db0f052-cb91-4170-81fd-107756b162f7", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "master-realm" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "e1d7f235-8bf2-40b8-be49-49aca70a5088", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + }, { + "id" : "e743f66a-2f56-4b97-b34b-33f06ff1e739", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "7174c175-1887-4e57-b95b-969fe040deff", + "attributes" : { } + } ], + "account" : [ { + "id" : "64d8f532-839e-4386-b2eb-fe8848b0a9de", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "attributes" : { } + }, { + "id" : "3ec22748-960f-4f96-a43e-50f54a02dc23", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "attributes" : { } + }, { + "id" : "177d18e4-46b0-4ea3-8b70-327486ce5bb2", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "attributes" : { } + }, { + "id" : "703643d6-0542-4e27-9737-7c442925c18c", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "attributes" : { } + }, { + "id" : "c64f9f66-d762-4337-8833-cf31c316e8a7", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "attributes" : { } + }, { + "id" : "611f568b-0fdd-4d2e-ba34-03136cd486c4", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRoles" : [ "offline_access", "uma_authorization" ], + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpSupportedApplications" : [ "FreeOTP", "Google Authenticator" ], + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account" ] + } ] + }, + "clients" : [ { + "id" : "a367038f-fe01-4459-9f91-7ad0cf498533", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/master/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "0896a464-da81-4454-bee9-b56bdbad9e7f", + "defaultRoles" : [ "view-profile", "manage-account" ], + "redirectUris" : [ "/realms/master/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "72f75604-1e21-407c-b967-790aafd11534", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/master/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "91f85142-ee18-4e30-9949-e5acb701bdee", + "redirectUris" : [ "/realms/master/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "2772c101-0dba-49b7-9627-5aaddc666939", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "b13fd0de-3be0-4a08-bc5d-d1de34421b1a", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "4640af2e-b4a6-44eb-85ec-6278a62a4f01", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "380985f1-61c7-4940-93ae-7a09458071ca", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "65d2ba2b-bcae-49ff-9f56-77c818f55930", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "7174c175-1887-4e57-b95b-969fe040deff", + "clientId" : "master-realm", + "name" : "master Realm", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "40f73851-a94c-4091-90de-aeee8ca1acf8", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, + { + "id": "0493c7c6-6e20-49ea-9acb-627c0b52d400", + "clientId": "oauth2-proxy", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "72341b6d-7065-4518-a0e4-50ee15025608", + "redirectUris": [ + "http://localhost:4180/oauth2/callback", + "http://localhost:4180" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "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.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "role_list", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, { + "id" : "2a3ad1fd-a30d-4b72-89c4-bed12f178338", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/master/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "b234b7aa-8417-410f-b3fd-c57434d3aa4a", + "redirectUris" : [ "/admin/master/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "5885b0d3-a917-4b52-8380-f37d0754a2ef", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "47ea3b67-4f0c-4c7e-8ac6-a33a3d655894", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "4be0ca19-0ec7-4cc1-b263-845ea539ff12", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "aba72e57-540f-4825-95b7-2d143be028cc", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "7fe82724-5748-4b6d-9708-a028f5d3b970", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "e42f334e-cfae-44a0-905d-c3ef215feaae", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "ec765598-bd71-4318-86c3-b3f81a41c99e", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "90694036-4014-4672-a2c8-c68319e9308a", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "f7b0fcc0-6139-4158-ac45-34fd9a58a5ef", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "8a09267b-3634-4a9c-baab-6f2fb4137347", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "3a48c5dd-33a8-4be0-9d2e-30fd7f98363a", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "5427d1b4-ba79-412a-b23c-da640a98980c", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "31d4a53f-6503-40e8-bd9d-79a7c46c4fbe", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "5921a9e9-7fec-4471-95e3-dd96eebdec58", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "4fa92092-ee0d-4dc7-a63b-1e3b02d35ebb", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "1a5cc2e2-c983-4150-8583-23a7f5c826bf", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "67931f77-722a-492d-b581-a953e26b7d44", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "10f6ac36-3a63-4e1c-ac69-c095588f5967", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "205d9dce-b6c8-4b1d-9c9c-fa24788651cf", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "638216c8-ea8c-40e3-9429-771e9278920e", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "39c17eae-8ea7-422c-ae21-b8876bf12184", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "01c559cf-94f2-46ad-b965-3b2e1db1a2a6", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "String" + } + }, { + "id" : "1693b5ab-28eb-485d-835d-2ae070ccb3ba", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "a0e08332-954c-46d2-9795-56eb31132580", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "cea0cd9c-d085-4d19-acc3-4bb41c891b68", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "3122097d-4cba-46c2-8b3b-5d87a4cc605e", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "a3b97897-d913-4e0a-a4cf-033ce78f7d24", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "a44eeb9d-410d-49c5-b0e0-5d84787627ad", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "651408a7-6704-4198-a60f-988821b633ea", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "a8c56c7b-ccbc-4b01-8df5-3ecb6328755f", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "13ec0fd3-e64a-4d6f-9be7-c8760f2c9d6b", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "75e741f8-dcd5-49d2-815e-8604ec1d08a1", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "06a2d506-4996-4a33-8c43-2cf64af6a630", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "3c3470df-d414-4e1c-87fc-3fb3cea34b8d", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "d85aba25-c74b-49e3-9ccb-77b4bb16efa5", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "86b3f64f-1525-4500-bcbc-9b889b25f995", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { } + } ] + } ], + "defaultDefaultClientScopes" : [ "roles", "profile", "role_list", "email", "web-origins" ], + "defaultOptionalClientScopes" : [ "phone", "address", "offline_access", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "xXSSProtection" : "1; mode=block", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "59048b39-ad0f-4d12-8c52-7cfc2c43278a", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ] + } + }, { + "id" : "760559a6-a59f-4175-9ac5-6f3612e20129", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "24f4cb42-76bd-499e-812a-4e0d270c9e13", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "abbfc599-480a-44ef-8e33-73a83eaab166", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper" ] + } + }, { + "id" : "3c6450f0-4521-402b-a247-c8165854b1fa", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "d9b64399-744b-498e-9d35-f68b1582bd7d", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "22f15f1f-3116-4348-a1e5-fc0d7576452a", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "4ad7b291-ddbb-4674-8c3d-ab8fd76d4168", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "f71cc325-9907-4d27-a0e6-88fca7450e5e", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "6c7d982e-372f-49c6-a4f3-5c451fb85eca" ], + "secret" : [ "yH6M3W7aOgh2_cKJ0srWbw" ], + "priority" : [ "100" ] + } + }, { + "id" : "7b50d0ab-dda5-4624-aa42-b4b397724ce1", + "name" : "hmac-generated", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "587f0fb5-845d-4b45-87a0-84145092aaef" ], + "secret" : [ "PuH8Lxh9GeNfGJRDk34SWIlBDdrJpC3U3SfcxqqQtlIf2DBzRKUu8VbDVrmMN5b5CoPsJhrQ2SVb-iE9Lzsb3A" ], + "priority" : [ "100" ], + "algorithm" : [ "HS256" ] + } + }, { + "id" : "547c1c71-9f97-4e12-801b-ed5c2cc61bba", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEAjdo2HZ5ruNnIbkSeAfFYpbPvJw3vtz/VuKJerC4mUXYd7qRMhs3VLJZ3mFyeCuO8W81vkGrFiC9KQnX2lHj2dtA/RWEJw5bpz+JdOFr5pvXg0lQ0sa+hro9afWDygTU4FmLsEi5z98847TbH178RT6n7+JVqZ9jYU9rSpwVTC8E/4yxSuStmhGCcAkZ6dGhHNBdvGUgwxKYj7dYLRJiI+nilIdKuxPzxI/YZxZnXBHDdbNXJgDymTQPut99OnBxeZbH38CJ1MNo3VdV1fzOMGUHe+vn/EOD5E+pXC8PwvJnWU+XHUTFVZeyIXehh3pYLUsq/6bZ1MYsEaFIhznOkwwIDAQABAoIBAHB+64fVyUxRurhoRn737fuLlU/9p2xGfbHtYvNdrhnQeLB3MBGAT10K/1Gfsd6k+Q49AAsiAgGcr2HBt4nL3HohcOwOpvWsS0UIGjHFRFP6jw9+pEN+K9UJ7xObvPZnRFHMpbdNi76tYlINrbMV3h61ihR8OmSc/gKSeZjnihK5OkaNnlqGRaBM/koI+iAxUHuJPnBLBZmD4T8eIfE4S2TvUeVeQogI9Muvnb9tIPJ5XyP9iXWLdRjnek/+wTdxHHZuo06Tc0bMjRaTHiF6K9ntOM2EmQb6bS2J47zgzRLNFE22BWH7RJq659EzElkOn0C0k7dWDTur/3Lpx1+zxJECgYEA8t+J3J+9oGTFmY2VPH05Yr/R/iVDOyIOlO1CmgonOQ3KPhbfNBB3aTAJP20LOZChN4JoWuiZJg4UwzXOeY9DvdDkPO0YLlSjPAIwJNk+xcxFcp5hqMUul2db+cgEY8zp0Wg9kFOq3JmJjK4+1+fgsVnOB+B08ZYI6bZzsUVKzucCgYEAlYTrsxs6fQua0cvZNQPYNZzwF3LVwPBl/ntkdRBE3HGyZeCAhIj7e9pAUusCPsQJaCmW2UEmywD/aIxGrBkojzTKItshM3PN1PYKL8W0Zq+H67uF5KfdvsbabZWHfP/LGCpoKF8Ov7JVPPqGrZ03Z2SheeLZAtNeHN4OB1u9i8UCgYATkS7qN3Rvl67T0DRVy0D0U7/3Wckw2m2SUgsrneXLEvFYTz9sUmdMcjJMidx9pslWT4tYx6SPDFNf5tXbtU8f29SHlBJ+qRL9oq9+SIJmLS7rLRdxIXG/gPRIC3VPFRNBa8SJ/DOn0jbivqcRffz8TN/sgojpbc0KB0kK3ypHwQKBgCKVCcb1R0PgyUA4+9YNO5a647UotFPZxl1jwMpqpuKt0WtKz67X2AK/ah1DidNmmB5lcCRzsztE0c4mk7n+X6kvtoj1UeqKoFLfTV/bRGxzsOZPCxrl0J3tdFvgN+QrbZf7Rvf/dHPWFWzzLO8+66+YUNjWJQdIR/45Rdlh2KdZAoGBAMfF3ir+fe3KdQ6hAf9QyrLxJ5l+GO+IgtxXGbon7eeJBIZHHdMeDy4pC7DMcI214BmIntbyY+xS+gI3oM26EJUVmrZ6tkyIDFsCHm9rcXG9ogvffzQWM1Wqzm27hR/3s+EPWW9AOcIimiFV1UPp/mLjnrCuq58V2aJS/TT14oLe" ], + "certificate" : [ "MIICmzCCAYMCBgFygL/j4DANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjAwNjA0MTkxMDU4WhcNMzAwNjA0MTkxMjM4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCN2jYdnmu42chuRJ4B8Vils+8nDe+3P9W4ol6sLiZRdh3upEyGzdUslneYXJ4K47xbzW+QasWIL0pCdfaUePZ20D9FYQnDlunP4l04Wvmm9eDSVDSxr6Guj1p9YPKBNTgWYuwSLnP3zzjtNsfXvxFPqfv4lWpn2NhT2tKnBVMLwT/jLFK5K2aEYJwCRnp0aEc0F28ZSDDEpiPt1gtEmIj6eKUh0q7E/PEj9hnFmdcEcN1s1cmAPKZNA+63306cHF5lsffwInUw2jdV1XV/M4wZQd76+f8Q4PkT6lcLw/C8mdZT5cdRMVVl7Ihd6GHelgtSyr/ptnUxiwRoUiHOc6TDAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIAqydMYxa51kNEyfXyR2kStlglE4LDeLBLHDABeBPE0eN2awoH/mw3kXS4OA/C0e3c7bAwViOzOVERGeUNiBvP5rL1Amuu97nwFcxhkTaJH4ZwCGkxceaIo9LNDpAEesqHLQSdplFXIA4TbEFoKMem4k31KVU7i9/rUesrSRmxLptIOK7LLvRMYiY/t7tdAvoZAtoliuQlFKQywEuxXQrCkcoVEAARABWGt0rsWC2xK0tVxHRIrENwvMp/aUYd17sZ0403aaS9dlvfQ63ExnaHd+++RJtPku8P220Tw27YVmFAwzJgS0aUpEaDsgRNz6OMSyxEg/n7eKK08aU3szwQ=" ], + "priority" : [ "100" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "3253f9b7-905d-4458-ad8a-8ada5e16d195", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "requirement" : "ALTERNATIVE", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 20, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "75bd854e-ab99-46f1-90ed-a8bfc1559558", + "alias" : "Authentication Options", + "description" : "Authentication options.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "basic-auth", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "basic-auth-otp", + "requirement" : "DISABLED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-spnego", + "requirement" : "DISABLED", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "9b0e6cce-62c5-4fb6-a48d-e07c950e38c3", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-otp-form", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "1c26fd14-ac06-4dc1-bdd8-8c34c1b41720", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "254f7549-51ec-4565-a736-35c07b6e25f0", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "auth-otp-form", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "b2413da8-3de9-4bfe-b77e-643fd1964c8f", + "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 + }, { + "requirement" : "REQUIRED", + "priority" : 20, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "f8392bfb-8dce-4a16-8af1-b2a4d1a0a273", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "reset-otp", + "requirement" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "fb69c297-b26e-44fa-aabd-d7b40eec3cd3", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "requirement" : "ALTERNATIVE", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "ALTERNATIVE", + "priority" : 20, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "de3a41a9-7018-4931-9c4d-d04f9501b2ce", + "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 + }, { + "requirement" : "CONDITIONAL", + "priority" : 20, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "6526b0d1-b48e-46c6-bb08-11ebcf458def", + "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" : "92a653ba-8f2d-4283-8354-ca55f9d89181", + "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 + }, { + "authenticator" : "client-secret-jwt", + "requirement" : "ALTERNATIVE", + "priority" : 30, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "authenticator" : "client-x509", + "requirement" : "ALTERNATIVE", + "priority" : 40, + "userSetupAllowed" : false, + "autheticatorFlow" : false + } ] + }, { + "id" : "e365be39-78db-46f0-b2e8-4e7001c2f5d0", + "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" : "REQUIRED", + "priority" : 20, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "CONDITIONAL", + "priority" : 30, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "dd61caf5-a40f-48b7-9e8c-a1f3b67041dd", + "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" : "7a055643-62e1-4ac1-b126-9a8d6c299635", + "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 + }, { + "requirement" : "REQUIRED", + "priority" : 20, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "fe8bc7ee-6e8f-436e-8336-c60fcd350843", + "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 + }, { + "requirement" : "CONDITIONAL", + "priority" : 20, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "3646f08e-ab70-415b-a701-6ed2e2d214c9", + "alias" : "http challenge", + "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "no-cookie-redirect", + "requirement" : "REQUIRED", + "priority" : 10, + "userSetupAllowed" : false, + "autheticatorFlow" : false + }, { + "requirement" : "REQUIRED", + "priority" : 20, + "flowAlias" : "Authentication Options", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "04176530-0972-47ad-83df-19d8534caac2", + "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" : "fa0ed569-6746-439e-b07e-89f7ed918c07", + "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" : "03680917-28f3-4ccd-bdf6-4a516f7c0018", + "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 + }, { + "requirement" : "CONDITIONAL", + "priority" : 40, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false, + "autheticatorFlow" : true + } ] + }, { + "id" : "19a9d9aa-2d2b-4701-807f-c384ab921c7e", + "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" : "534f01f4-45b3-43a0-91d1-238860cc126d", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "65bb9337-9633-4a21-8f6f-1d4129f664ac", + "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, + "priority" : 10, + "config" : { } + }, { + "alias" : "terms_and_conditions", + "name" : "Terms and Conditions", + "providerId" : "terms_and_conditions", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "attributes" : { }, + "keycloakVersion" : "10.0.0", + "userManagedAccessAllowed" : false +} + diff --git a/deploy/oauth/keycloak/master-users-0.json b/deploy/oauth/keycloak/master-users-0.json new file mode 100644 index 000000000..69415e7f0 --- /dev/null +++ b/deploy/oauth/keycloak/master-users-0.json @@ -0,0 +1,31 @@ +{ + "realm": "master", + "users": [ + { + "id": "admin@datavisyn.io", + "createdTimestamp": 1591297959169, + "username": "admin", + "email": "admin@datavisyn.io", + "enabled": true, + "totp": false, + "emailVerified": true, + "credentials": [ + { + "id": "a1a06ecd-fdc0-4e67-92cd-2da22d724e32", + "type": "password", + "createdDate": 1591297959315, + "secretData": "{\"value\":\"6rt5zuqHVHopvd0FTFE0CYadXTtzY0mDY2BrqnNQGS51/7DfMJeGgj0roNnGMGvDv30imErNmiSOYl+cL9jiIA==\",\"salt\":\"LI0kqr09JB7J9wvr2Hxzzg==\"}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["offline_access", "admin", "uma_authorization"], + "clientRoles": { + "account": ["view-profile", "manage-account"] + }, + "notBefore": 0, + "groups": [] + } + ] +} diff --git a/deploy/oauth/oauth2-proxy.cfg b/deploy/oauth/oauth2-proxy.cfg new file mode 100644 index 000000000..731e022a6 --- /dev/null +++ b/deploy/oauth/oauth2-proxy.cfg @@ -0,0 +1,25 @@ +# modified variant of oauth2-proxy-keycloak.cfg +# https://github.com/oauth2-proxy/oauth2-proxy/blob/master/contrib/local-environment/oauth2-proxy-keycloak.cfg +http_address="0.0.0.0:4180" +cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w=" +email_domains=["*"] +cookie_secure="false" +upstreams="http://localhost:8080" +redirect_url="http://localhost:4180/oauth2/callback" + +# keycloak provider +client_secret="72341b6d-7065-4518-a0e4-50ee15025608" +client_id="oauth2-proxy" +whitelist_domains=["keycloak:9080"] + +oidc_issuer_url="http://keycloak:9080/auth/realms/master" +provider="oidc" +provider_display_name="Keycloak" + +# custom config, other than default +# https://github.com/oauth2-proxy/oauth2-proxy/issues/843#issuecomment-717212451 +# Header: Authorization: Bearer {jwt} +pass_authorization_header=true +# Header: X-Forwarded-Access-Token +pass_access_token=true +skip_provider_button = true diff --git a/package.json b/package.json index cc0f7477c..038b62801 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "visyn_core", "description": "Core repository for datavisyn applications.", - "version": "3.1.1-SNAPSHOT", + "version": "4.0.1-SNAPSHOT", "author": { "name": "datavisyn GmbH", "email": "contact@datavisyn.io", diff --git a/requirements.txt b/requirements.txt index 4d8d05c68..7067d4b0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,35 @@ # a2wsgi==1.6.0 # This WSIGMiddleware is not compatible with starlette_context -alembic==1.9.4 -cachetools==5.3.0 -fastapi[all]==0.92.0 +alembic==1.11.1 +cachetools==5.3.1 +fastapi==0.101.1 Flask[async]>=2.1.0,<=2.2.2 json-cfg==0.4.2 -marshmallow-sqlalchemy>=0.26.0,<=0.28.1 -marshmallow==3.19.0 -openpyxl==3.0.9 -opentelemetry-api==1.18.0 -opentelemetry-exporter-otlp==1.18.0 +openpyxl==3.1.2 +opentelemetry-api==1.19.0 +opentelemetry-exporter-otlp==1.19.0 opentelemetry-exporter-prometheus==1.12.0rc1 -opentelemetry-instrumentation-fastapi==0.39b0 -opentelemetry-instrumentation-httpx==0.39b0 -opentelemetry-instrumentation-logging==0.39b0 -opentelemetry-instrumentation-requests==0.39b0 -opentelemetry-instrumentation-sqlalchemy==0.39b0 -opentelemetry-instrumentation-system-metrics==0.39b0 -opentelemetry-sdk==1.18.0 -psycopg==3.1.8 -psycopg2==2.9.5 -pydantic==1.10.5 -pyjwt[crypto]==2.6.0 -pytest-postgresql==4.1.1 +opentelemetry-instrumentation-fastapi==0.40b0 +opentelemetry-instrumentation-httpx==0.40b0 +opentelemetry-instrumentation-logging==0.40b0 +opentelemetry-instrumentation-requests==0.40b0 +opentelemetry-instrumentation-sqlalchemy==0.40b0 +opentelemetry-instrumentation-system-metrics==0.40b0 +opentelemetry-sdk==1.19.0 +psycopg==3.1.9 +psycopg2==2.9.6 +pydantic==1.10.11 +pyjwt[crypto]==2.8.0 +pytest-postgresql==5.0.0 python-dateutil==2.8.2 -python-multipart==0.0.5 -requests==2.28.2 -SQLAlchemy>=1.4.40,<=1.4.46 +python-multipart==0.0.6 +requests==2.31.0 +SQLAlchemy>=1.4.40,<=1.4.49 starlette-context==0.3.6 -uvicorn[standard]==0.20.0 +uvicorn[standard]==0.23.1 +# Extras from fastapi[all], which we can't install because it requires pydantic v2: https://github.com/tiangolo/fastapi/blob/f7e3559bd5997f831fb9b02bef9c767a50facbc3/pyproject.toml#L57-L67 +httpx>=0.23.0 +jinja2>=2.11.2 +itsdangerous>=1.1.0 +pyyaml>=5.3.1 +ujson>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0 +orjson>=3.2.1 diff --git a/src/app/login/VisynLoginMenu.tsx b/src/app/login/VisynLoginMenu.tsx index 7e49736f7..014527a34 100644 --- a/src/app/login/VisynLoginMenu.tsx +++ b/src/app/login/VisynLoginMenu.tsx @@ -2,7 +2,6 @@ import React, { useState } from 'react'; import { Alert, Modal, Stack, Title, Center, Divider, Container, LoadingOverlay, Anchor } from '@mantine/core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons/faCircleExclamation'; -import { appContext } from '../../base/AppContext'; import { userSession } from '../../security/UserSession'; import { LoginUtils } from '../../security/LoginUtils'; import { SessionWatcher } from '../../security/watcher'; @@ -13,7 +12,7 @@ import { DefaultLoginForm, UserStoreUIMap } from './UserStoreUIMap'; export function VisynLoginMenu({ watch = false }: { watch?: boolean }) { const { appName, user } = useVisynAppContext(); const [show, setShow] = useState(false); - const [error, setError] = useState(null); + const [error, setError] = useState(null); React.useEffect(() => { if (watch) { @@ -73,6 +72,7 @@ export function VisynLoginMenu({ watch = false }: { watch?: boolean }) { component="button" type="button" onClick={() => { + setError(null); retryGetStores(); }} > diff --git a/src/utils/fromNow.ts b/src/utils/fromNow.ts index 8d97b69f8..681656893 100644 --- a/src/utils/fromNow.ts +++ b/src/utils/fromNow.ts @@ -1,4 +1,4 @@ -const formatter = new Intl.RelativeTimeFormat(undefined, { +const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto', }); diff --git a/visyn_core/dbmanager.py b/visyn_core/dbmanager.py index 7dc284ab5..f2e68e001 100644 --- a/visyn_core/dbmanager.py +++ b/visyn_core/dbmanager.py @@ -3,7 +3,7 @@ from fastapi import FastAPI from sqlalchemy.engine import Engine -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, sessionmaker from . import manager from .dbview import DBConnector @@ -17,8 +17,8 @@ class DBManager: def __init__(self): self.connectors: dict[str, DBConnector] = {} self._plugins = {} - self._engines = {} - self._sessionmakers = {} + self._engines: dict[str, Engine] = {} + self._sessionmakers: dict[Engine, sessionmaker] = {} def init_app(self, app: FastAPI): app.add_middleware(CloseWebSessionsMiddleware) diff --git a/visyn_core/rdkit/img_api.py b/visyn_core/rdkit/img_api.py index e29dcea3b..bdb3e746f 100644 --- a/visyn_core/rdkit/img_api.py +++ b/visyn_core/rdkit/img_api.py @@ -13,7 +13,7 @@ from .util.draw import draw, draw_similarity from .util.molecule import aligned, maximum_common_substructure_query_mol -app = APIRouter(prefix="/api/rdkit", tags=["images"]) +app = APIRouter(prefix="/api/rdkit", tags=["RDKit"]) @app.get("/", response_class=SvgResponse) diff --git a/visyn_core/security/store/alb_security_store.py b/visyn_core/security/store/alb_security_store.py index 064752931..ca9490429 100644 --- a/visyn_core/security/store/alb_security_store.py +++ b/visyn_core/security/store/alb_security_store.py @@ -27,7 +27,7 @@ def __init__( self, cookie_name: str | None, signout_url: str | None, - email_token_field: str, + email_token_field: str | list[str], audience: str | list[str] | None, issuer: str | None, decode_options: dict[str, Any] | None, @@ -36,7 +36,7 @@ def __init__( ): self.cookie_name = cookie_name self.signout_url = signout_url - self.email_token_field = email_token_field + self.email_token_fields = [email_token_field] if isinstance(email_token_field, str) else email_token_field self.audience = audience self.issuer = issuer self.decode_options = decode_options @@ -66,9 +66,15 @@ def load_from_request(self, req: Request): algorithms=self.decode_algorithms, ) + # Go through all the fields we want to check for the user id + id = next((user.get(field, None) for field in self.email_token_fields if user.get(field, None)), None) + if not id: + _log.error(f"No {self.email_token_fields} matched in token, possible fields: {user.keys()}") + return None + # Create new user from given attributes return User( - id=user[self.email_token_field], + id=id, roles=user.get("roles", []), oauth2_access_token=req.headers["X-Amzn-Oidc-Accesstoken"], ) diff --git a/visyn_core/security/store/oauth2_security_store.py b/visyn_core/security/store/oauth2_security_store.py index d80a7e708..031151a62 100644 --- a/visyn_core/security/store/oauth2_security_store.py +++ b/visyn_core/security/store/oauth2_security_store.py @@ -13,9 +13,10 @@ class OAuth2SecurityStore(BaseStore): ui = "AutoLoginForm" - def __init__(self, cookie_name: str | None, signout_url: str | None): + def __init__(self, cookie_name: str | None, signout_url: str | None, email_token_field: str | list[str]): self.cookie_name = cookie_name self.signout_url: str | None = signout_url + self.email_token_fields = [email_token_field] if isinstance(email_token_field, str) else email_token_field def load_from_request(self, req: Request): token_field = manager.settings.visyn_core.security.store.oauth2_security_store.access_token_header_name @@ -25,10 +26,16 @@ def load_from_request(self, req: Request): if access_token: # Try to decode the oidc data jwt user = jwt.decode(access_token, options={"verify_signature": False}) + + # Go through all the fields we want to check for the user id + id = next((user.get(field, None) for field in self.email_token_fields if user.get(field, None)), None) + if not id: + _log.error(f"No {self.email_token_fields} matched in token, possible fields: {user.keys()}") + return None + # Create new user from given attributes - email = user[manager.settings.visyn_core.security.store.oauth2_security_store.email_token_field] return User( - id=email, + id=id, roles=[], oauth2_access_token=access_token, ) @@ -60,6 +67,7 @@ def create(): return OAuth2SecurityStore( manager.settings.visyn_core.security.store.oauth2_security_store.cookie_name, manager.settings.visyn_core.security.store.oauth2_security_store.signout_url, + manager.settings.visyn_core.security.store.oauth2_security_store.email_token_field, ) return None diff --git a/visyn_core/server/visyn_server.py b/visyn_core/server/visyn_server.py index 3b0a0b97c..0dee41743 100644 --- a/visyn_core/server/visyn_server.py +++ b/visyn_core/server/visyn_server.py @@ -4,7 +4,6 @@ import threading from typing import Any -import anyio from fastapi import FastAPI from fastapi.middleware.wsgi import WSGIMiddleware from pydantic import create_model @@ -164,8 +163,15 @@ def filter(self, record: logging.LogRecord) -> bool: async def change_anyio_total_tokens(): # FastAPI uses anyio threads to handle sync endpoint concurrently. # This is a workaround to increase the number of threads to 100, as the default is only 40. - limiter = anyio.to_thread.current_default_thread_limiter() - limiter.total_tokens = manager.settings.visyn_core.total_anyio_tokens + try: + from anyio import to_thread + + limiter = to_thread.current_default_thread_limiter() + limiter.total_tokens = manager.settings.visyn_core.total_anyio_tokens + except Exception as e: + _log.exception( + f"Could not set the total number of anyio tokens to {manager.settings.visyn_core.total_anyio_tokens}. Error: {e}" + ) if manager.settings.visyn_core.cypress: _log.info("Cypress mode is enabled. This should only be used in a Cypress testing environment or CI.") diff --git a/visyn_core/settings/model.py b/visyn_core/settings/model.py index 0b4ef743e..8e8ddfe71 100644 --- a/visyn_core/settings/model.py +++ b/visyn_core/settings/model.py @@ -1,8 +1,8 @@ import contextlib -import json from typing import Any, Literal -from pydantic import AnyHttpUrl, BaseModel, BaseSettings, Extra, Field, validator +from pydantic import AnyHttpUrl, BaseConfig, BaseModel, BaseSettings, Extra, Field +from pydantic.env_settings import EnvSettingsSource, SettingsSourceCallable from .constants import default_logging_dict @@ -44,7 +44,7 @@ class AlbSecurityStoreSettings(BaseModel): enable: bool = False cookie_name: str | None = None signout_url: str | None = None - email_token_field: str = "email" + email_token_field: str | list[str] = ["email"] """ Field in the JWT token that contains the email address of the user. """ @@ -76,7 +76,7 @@ class OAuth2SecurityStoreSettings(BaseModel): cookie_name: str | None = None signout_url: str | None = None access_token_header_name: str = "X-Forwarded-Access-Token" - email_token_field: str = "email" + email_token_field: str | list[str] = ["email"] class NoSecurityStoreSettings(BaseModel): @@ -110,15 +110,6 @@ class BaseExporterTelemetrySettings(BaseModel): timeout: int | None = None kwargs: dict[str, Any] = {} - @validator("headers", pre=True) - def json_decode_headers(cls, v): # NOQA N805 - # Manually parse JSON strings if they are coming from the env via `VISYN_CORE__...='{"...": ...}'`. - # See https://github.com/pydantic/pydantic/issues/831 for details. - if isinstance(v, str): - with contextlib.suppress(ValueError): - return json.loads(v) - return v - class MetricsExporterTelemetrySettings(BaseExporterTelemetrySettings): pass @@ -211,15 +202,6 @@ class VisynCoreSettings(BaseModel): client_config: dict[str, Any] | None = None """Client config to be loaded via /api/v1/visyn/clientConfig""" - @validator("client_config", pre=True) - def json_decode_client_config(cls, v): # NOQA N805 - # Manually parse JSON strings if they are coming from the env via `VISYN_CORE__CLIENT_CONFIG='{"...": ...}'`. - # See https://github.com/pydantic/pydantic/issues/831 for details. - if isinstance(v, str): - with contextlib.suppress(ValueError): - return json.loads(v) - return v - class GlobalSettings(BaseSettings): env: Literal["development", "production"] = "production" @@ -255,6 +237,54 @@ def get_nested(self, key: str, default: Any = None) -> Any | None: dic = dic.get(key, None) if dic else None return dic if dic is not None else default - class Config: + class Config(BaseConfig): extra = Extra.allow env_nested_delimiter = "__" + + @classmethod + def customise_sources( + cls, + init_settings: SettingsSourceCallable, + env_settings: EnvSettingsSource, + file_secret_settings: SettingsSourceCallable, + ) -> tuple[SettingsSourceCallable, ...]: + class EnvSettingsSourceWithJSON(EnvSettingsSource): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + """ + String vars that end with this suffix are not converted to JSON. + """ + self.as_string_suffix = "_as_string" + + def explode_env_vars(self, *args, **kwargs) -> dict[str, Any]: + """ + This may be hacky, but it allows us to load JSON strings from the environment variables, even for deeply nested models. + The original explode_env_vars simply stores the values as strings, and does not load them as JSON. + Here, we patch it and iterate over all values, and try to load them as JSON. + TODO: This may have one negative side effect: what if my value is typed as a string, but we now convert it to a dict? + You can suffix the variable with `_as_string` to prevent this. + """ + exploded = super().explode_env_vars(*args, **kwargs) + + def str_to_dict(d: dict[str, Any]): + for k in list(d.keys()): + if isinstance(d[k], dict): + str_to_dict(d[k]) + elif isinstance(d[k], str): + if k.lower().endswith(self.as_string_suffix): + # If the value ends with the suffix, add the string value without the suffix to the dict. + d[k.lower().removesuffix(self.as_string_suffix)] = d[k] + else: + # Otherwise, try to load the value as JSON. + with contextlib.suppress(ValueError): + d[k] = cls.json_loads(d[k]) + return d + + return str_to_dict(exploded) + + return ( + init_settings, + # Instead of the default EnvSettingsSource, use our custom one. + EnvSettingsSourceWithJSON(**{s: getattr(env_settings, s) for s in env_settings.__slots__}), + file_secret_settings, + ) diff --git a/visyn_core/tests/test_security_login.py b/visyn_core/tests/test_security_login.py index 55383e55e..2ae0cae7a 100644 --- a/visyn_core/tests/test_security_login.py +++ b/visyn_core/tests/test_security_login.py @@ -120,6 +120,7 @@ def test_jwt_token_location(client: TestClient): def test_alb_security_store(client: TestClient): # Add some basic configuration manager.settings.visyn_core.security.store.alb_security_store.enable = True + manager.settings.visyn_core.security.store.alb_security_store.email_token_field = ["field1", "field2", "email"] manager.settings.visyn_core.security.store.alb_security_store.decode_options = {"verify_signature": False} manager.settings.visyn_core.security.store.alb_security_store.cookie_name = "TestCookie" manager.settings.visyn_core.security.store.alb_security_store.signout_url = "http://localhost/logout" @@ -150,6 +151,10 @@ def test_alb_security_store(client: TestClient): assert response.status_code == 200 assert response.json()["redirect"] == "http://localhost/logout" + # Test if we are not logged in if we use invalid fields + store.email_token_fields = ["field1", "field2"] + assert client.get("/loggedinas", headers=headers).json() == '"not_yet_logged_in"' + def test_oauth2_security_store(client: TestClient): # Add some basic configuration diff --git a/visyn_core/tests/test_settings.py b/visyn_core/tests/test_settings.py index 0795dfcd1..b52d8f6d3 100644 --- a/visyn_core/tests/test_settings.py +++ b/visyn_core/tests/test_settings.py @@ -14,8 +14,12 @@ def test_env_substitution(): assert settings.secret_key != "Custom_Secret_Key" assert settings.visyn_core.security.store.alb_security_store.enable != True # NOQA: E712 + assert settings.visyn_core.security.store.alb_security_store.cookie_name is None + assert settings.visyn_core.security.store.alb_security_store.email_token_field == ["email"] + assert settings.visyn_core.security.store.oauth2_security_store.email_token_field == ["email"] assert settings.visyn_core.logging["version"] == 1 assert settings.visyn_core.logging["root"]["level"] == "INFO" + assert settings.visyn_core.client_config is None with mock.patch.dict( os.environ, @@ -23,10 +27,18 @@ def test_env_substitution(): # Basic top-level key substitution "SECRET_KEY": "Custom_Secret_Key", # Deeply nested key substitution of properly typed model (includes automatic typecast) - "visyn_core__SECURITY__STORE__ALB_SECURITY_STORE__ENABLE": "True", + "VISYN_CORE__SECURITY__STORE__ALB_SECURITY_STORE__ENABLE": "True", + # Deeply nested key substitution without automatic dict conversion + "VISYN_CORE__SECURITY__STORE__ALB_SECURITY_STORE__COOKIE_NAME_AS_STRING": '{"my": "dict"}', + # Deeply nested key substitution with automatic dict conversion + "VISYN_CORE__SECURITY__STORE__ALB_SECURITY_STORE__EMAIL_TOKEN_FIELD": '["field1", "email"]', + "VISYN_CORE__SECURITY__STORE__OAUTH2_SECURITY_STORE__EMAIL_TOKEN_FIELD": "field1", # Deeply nested key substitution of model typed via Dict (does not include automatic typecast) - "visyn_core__LOGGING__VERSION": "2", - "visyn_core__LOGGING__ROOT__LEVEL": "DEBUG", + "VISYN_CORE__LOGGING__VERSION": "2", + "VISYN_CORE__LOGGING__VERSION_AS_STRING_AS_STRING": "2", + "VISYN_CORE__LOGGING__ROOT__LEVEL": "DEBUG", + # Load dict types from JSON strings. Important: it must be a valid JSON, i.e. no \" escaping or so. Use single quotes to enclose the variable. + "VISYN_CORE__CLIENT_CONFIG": '{"demo_from_function": true, "demo_from_class": true}', }, clear=True, ): @@ -34,14 +46,19 @@ def test_env_substitution(): assert env_settings.secret_key == "Custom_Secret_Key" assert env_settings.visyn_core.security.store.alb_security_store.enable == True # NOQA: E712 - assert env_settings.visyn_core.logging["version"] == "2" # Note that this is a string, as it cannot infer the type of Dict + assert env_settings.visyn_core.security.store.alb_security_store.cookie_name == '{"my": "dict"}' + assert env_settings.visyn_core.security.store.alb_security_store.email_token_field == ["field1", "email"] + assert env_settings.visyn_core.security.store.oauth2_security_store.email_token_field == "field1" + assert env_settings.visyn_core.logging["version"] == 2 assert env_settings.visyn_core.logging["root"]["level"] == "DEBUG" + assert env_settings.visyn_core.client_config == {"demo_from_function": True, "demo_from_class": True} assert env_settings.get_nested("secret_key") == "Custom_Secret_Key" assert env_settings.get_nested("visyn_core.security.store.alb_security_store.enable") == True # NOQA: E712 - assert ( - env_settings.get_nested("visyn_core.logging.version") == "2" - ) # Note that this is a string, as it cannot infer the type of Dict + assert env_settings.get_nested("visyn_core.logging.version") == 2 + # Both variants as string are preserved, in case a real setting ends with the suffix + assert env_settings.get_nested("visyn_core.logging.version_as_string") == "2" + assert env_settings.get_nested("visyn_core.logging.version_as_string_as_string") == "2" assert env_settings.get_nested("visyn_core.logging.root.level") == "DEBUG"