diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4ad47cb5..0131c2d9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,63 +9,84 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: "CodeQL Advanced" on: push: - branches: [ main ] + branches: ["main"] pull_request: - # The branches below must be a subset of the branches above - branches: [ main ] + branches: ["main"] schedule: - - cron: '15 16 * * 1' + - cron: "15 16 * * 1" jobs: analyze: - name: Analyze - runs-on: ubuntu-latest + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ 'ubuntu-latest' }} permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories actions: read contents: read - security-events: write strategy: fail-fast: false matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - + include: + - language: go + build-mode: autobuild + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + - name: Checkout repository + uses: actions/checkout@v4 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality - #- run: | - # make bootstrap - # make release + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9dc4aacc..e8383359 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,13 +7,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup Go environment - uses: actions/setup-go@v3.0.0 + uses: actions/setup-go@v5.0.2 with: - go-version: 1.19 + go-version: 1.23 - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4.1.7 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v3.1.0 + uses: golangci/golangci-lint-action@v6.1.0 - name: WriteGoList run: go list -json -m all > go.list - name: nancy @@ -33,14 +33,14 @@ jobs: docker ps docker logs keycloak - name: CodeCov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v4.5.0 with: file: ./coverage.txt # Publish benchmarks for the main branch only - name: Store Benchmark Result if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: rhysd/github-action-benchmark@v1 + uses: rhysd/github-action-benchmark@v1.20.3 with: # What benchmark tool the output.txt came from tool: "go" diff --git a/.golangci.yml b/.golangci.yml index d59a6c32..03626a58 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,8 +1,3 @@ -run: - skip-dirs: - - (^|/)testdata($|/) - skip-dirs-use-default: false - linters: enable: - goimports @@ -18,10 +13,6 @@ linters: linters-settings: misspell: locale: US - golint: - min-confidence: 0 - govet: - check-shadowing: false goimports: local-prefixes: github.com/Nerzal/gocloak gocognit: @@ -32,6 +23,9 @@ linters-settings: simplify: true issues: + exclude-dirs: + - (^|/)testdata($|/) + exclude-dirs-use-default: false exclude-use-default: false exclude-rules: - path: _test\.go diff --git a/Dockerfile b/Dockerfile index b9fe4acc..9d2c940a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/keycloak/keycloak:24.0.3 +FROM quay.io/keycloak/keycloak:26.5.1 COPY testdata data/import WORKDIR /opt/keycloak ENV KC_HOSTNAME=localhost @@ -6,6 +6,7 @@ ENV KEYCLOAK_USER=admin ENV KEYCLOAK_PASSWORD=secret ENV KEYCLOAK_ADMIN=admin ENV KEYCLOAK_ADMIN_PASSWORD=secret -ENV KC_FEATURES=account-api,account2,authorization,client-policies,impersonation,docker,scripts,admin-fine-grained-authz +ENV KC_HEALTH_ENABLED=true +ENV KC_FEATURES=account-api,authorization,client-policies,impersonation,docker,scripts,admin-fine-grained-authz RUN /opt/keycloak/bin/kc.sh import --file /data/import/gocloak-realm.json ENTRYPOINT ["/opt/keycloak/bin/kc.sh"] diff --git a/Makefile b/Makefile index 0409e817..b32a0b54 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,11 @@ test: ./run-tests.sh start-keycloak: stop-keycloak - docker-compose up -d + docker compose up -d stop-keycloak: - docker-compose down + docker compose down + +generate-gocloak-interface: + @echo "Remember to: go install github.com/vburenin/ifacemaker@latest" + @$(shell go env GOPATH)/bin/ifacemaker -f client.go -s GoCloak -i GoCloakIface -p gocloak -o gocloak_iface.go diff --git a/README.md b/README.md index 6f4d7c09..0138cc78 100644 --- a/README.md +++ b/README.md @@ -116,258 +116,8 @@ To get the `clientId` from `id`, use `GetClients` method with `GetClientsParams{ ## Features -```go -// GoCloak holds all methods a client should fulfill -type GoCloak interface { - - RestyClient() *resty.Client - SetRestyClient(restyClient *resty.Client) - - GetToken(ctx context.Context, realm string, options TokenOptions) (*JWT, error) - GetRequestingPartyToken(ctx context.Context, token, realm string, options RequestingPartyTokenOptions) (*JWT, error) - GetRequestingPartyPermissions(ctx context.Context, token, realm string, options RequestingPartyTokenOptions) (*[]RequestingPartyPermission, error) - GetRequestingPartyPermissionDecision(ctx context.Context, token, realm string, options RequestingPartyTokenOptions) (*RequestingPartyPermissionDecision, error) - - Login(ctx context.Context, clientID, clientSecret, realm, username, password string) (*JWT, error) - LoginOtp(ctx context.Context, clientID, clientSecret, realm, username, password, totp string) (*JWT, error) - Logout(ctx context.Context, clientID, clientSecret, realm, refreshToken string) error - LogoutPublicClient(ctx context.Context, clientID, realm, accessToken, refreshToken string) error - LogoutAllSessions(ctx context.Context, accessToken, realm, userID string) error - RevokeUserConsents(ctx context.Context, accessToken, realm, userID, clientID string) error - LogoutUserSession(ctx context.Context, accessToken, realm, session string) error - LoginClient(ctx context.Context, clientID, clientSecret, realm string) (*JWT, error) - LoginClientSignedJWT(ctx context.Context, clientID, realm string, key interface{}, signedMethod jwt.SigningMethod, expiresAt *jwt.Time) (*JWT, error) - LoginAdmin(ctx context.Context, username, password, realm string) (*JWT, error) - RefreshToken(ctx context.Context, refreshToken, clientID, clientSecret, realm string) (*JWT, error) - DecodeAccessToken(ctx context.Context, accessToken, realm, expectedAudience string) (*jwt.Token, *jwt.MapClaims, error) - DecodeAccessTokenCustomClaims(ctx context.Context, accessToken, realm, expectedAudience string, claims jwt.Claims) (*jwt.Token, error) - RetrospectToken(ctx context.Context, accessToken, clientID, clientSecret, realm string) (*RetrospecTokenResult, error) - GetIssuer(ctx context.Context, realm string) (*IssuerResponse, error) - GetCerts(ctx context.Context, realm string) (*CertResponse, error) - GetServerInfo(ctx context.Context, accessToken string) (*ServerInfoRepesentation, error) - GetUserInfo(ctx context.Context, accessToken, realm string) (*UserInfo, error) - GetRawUserInfo(ctx context.Context, accessToken, realm string) (map[string]interface{}, error) - SetPassword(ctx context.Context, token, userID, realm, password string, temporary bool) error - ExecuteActionsEmail(ctx context.Context, token, realm string, params ExecuteActionsEmail) error - - CreateUser(ctx context.Context, token, realm string, user User) (string, error) - CreateGroup(ctx context.Context, accessToken, realm string, group Group) (string, error) - CreateChildGroup(ctx context.Context, token, realm, groupID string, group Group) (string, error) - CreateClientRole(ctx context.Context, accessToken, realm, idOfClient string, role Role) (string, error) - CreateClient(ctx context.Context, accessToken, realm string, newClient Client) (string, error) - CreateClientScope(ctx context.Context, accessToken, realm string, scope ClientScope) (string, error) - CreateComponent(ctx context.Context, accessToken, realm string, component Component) (string, error) - CreateClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string, roles []Role) error - CreateClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error - CreateClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfCLientScope string, roles []Role) error - CreateClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role) error - - UpdateUser(ctx context.Context, accessToken, realm string, user User) error - UpdateGroup(ctx context.Context, accessToken, realm string, updatedGroup Group) error - UpdateRole(ctx context.Context, accessToken, realm, idOfClient string, role Role) error - UpdateClient(ctx context.Context, accessToken, realm string, updatedClient Client) error - UpdateClientScope(ctx context.Context, accessToken, realm string, scope ClientScope) error - - DeleteUser(ctx context.Context, accessToken, realm, userID string) error - DeleteComponent(ctx context.Context, accessToken, realm, componentID string) error - DeleteGroup(ctx context.Context, accessToken, realm, groupID string) error - DeleteClientRole(ctx context.Context, accessToken, realm, idOfClient, roleName string) error - DeleteClientRoleFromUser(ctx context.Context, token, realm, idOfClient, userID string, roles []Role) error - DeleteClient(ctx context.Context, accessToken, realm, idOfClient string) error - DeleteClientScope(ctx context.Context, accessToken, realm, scopeID string) error - DeleteClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string, roles []Role) error - DeleteClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error - DeleteClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfCLientScope string, roles []Role) error - DeleteClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, ifOfClient string, roles []Role) error - - GetClient(ctx context.Context, accessToken, realm, idOfClient string) (*Client, error) - GetClientsDefaultScopes(ctx context.Context, token, realm, idOfClient string) ([]*ClientScope, error) - AddDefaultScopeToClient(ctx context.Context, token, realm, idOfClient, scopeID string) error - RemoveDefaultScopeFromClient(ctx context.Context, token, realm, idOfClient, scopeID string) error - GetClientsOptionalScopes(ctx context.Context, token, realm, idOfClient string) ([]*ClientScope, error) - AddOptionalScopeToClient(ctx context.Context, token, realm, idOfClient, scopeID string) error - RemoveOptionalScopeFromClient(ctx context.Context, token, realm, idOfClient, scopeID string) error - GetDefaultOptionalClientScopes(ctx context.Context, token, realm string) ([]*ClientScope, error) - GetDefaultDefaultClientScopes(ctx context.Context, token, realm string) ([]*ClientScope, error) - GetClientScope(ctx context.Context, token, realm, scopeID string) (*ClientScope, error) - GetClientScopes(ctx context.Context, token, realm string) ([]*ClientScope, error) - GetClientScopeMappings(ctx context.Context, token, realm, idOfClient string) (*MappingsRepresentation, error) - GetClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string) ([]*Role, error) - GetClientScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClient string) ([]*Role, error) - GetClientScopesScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClientScope string) ([]*Role, error) - GetClientScopesScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error) - GetClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error) - GetClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClientScope string) ([]*Role, error) - GetClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error) - GetClientScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error) - GetClientSecret(ctx context.Context, token, realm, idOfClient string) (*CredentialRepresentation, error) - GetClientServiceAccount(ctx context.Context, token, realm, idOfClient string) (*User, error) - RegenerateClientSecret(ctx context.Context, token, realm, idOfClient string) (*CredentialRepresentation, error) - GetKeyStoreConfig(ctx context.Context, accessToken, realm string) (*KeyStoreConfig, error) - GetUserByID(ctx context.Context, accessToken, realm, userID string) (*User, error) - GetUserCount(ctx context.Context, accessToken, realm string, params GetUsersParams) (int, error) - GetUsers(ctx context.Context, accessToken, realm string, params GetUsersParams) ([]*User, error) - GetUserGroups(ctx context.Context, accessToken, realm, userID string, params GetGroupsParams) ([]*UserGroup, error) - AddUserToGroup(ctx context.Context, token, realm, userID, groupID string) error - DeleteUserFromGroup(ctx context.Context, token, realm, userID, groupID string) error - GetComponents(ctx context.Context, accessToken, realm string) ([]*Component, error) - GetGroups(ctx context.Context, accessToken, realm string, params GetGroupsParams) ([]*Group, error) - GetGroupsCount(ctx context.Context, token, realm string, params GetGroupsParams) (int, error) - GetGroup(ctx context.Context, accessToken, realm, groupID string) (*Group, error) - GetDefaultGroups(ctx context.Context, accessToken, realm string) ([]*Group, error) - AddDefaultGroup(ctx context.Context, accessToken, realm, groupID string) error - RemoveDefaultGroup(ctx context.Context, accessToken, realm, groupID string) error - GetGroupMembers(ctx context.Context, accessToken, realm, groupID string, params GetGroupsParams) ([]*User, error) - GetRoleMappingByGroupID(ctx context.Context, accessToken, realm, groupID string) (*MappingsRepresentation, error) - GetRoleMappingByUserID(ctx context.Context, accessToken, realm, userID string) (*MappingsRepresentation, error) - GetClientRoles(ctx context.Context, accessToken, realm, idOfClient string, params GetRoleParams) ([]*Role, error) - GetClientRole(ctx context.Context, token, realm, idOfClient, roleName string) (*Role, error) - GetClientRoleByID(ctx context.Context, accessToken, realm, roleID string) (*Role, error) - GetClients(ctx context.Context, accessToken, realm string, params GetClientsParams) ([]*Client, error) - AddClientRoleComposite(ctx context.Context, token, realm, roleID string, roles []Role) error - DeleteClientRoleComposite(ctx context.Context, token, realm, roleID string, roles []Role) error - GetUsersByRoleName(ctx context.Context, token, realm, roleName string) ([]*User, error) - GetUsersByClientRoleName(ctx context.Context, token, realm, idOfClient, roleName string, params GetUsersByRoleParams) ([]*User, error) - CreateClientProtocolMapper(ctx context.Context, token, realm, idOfClient string, mapper ProtocolMapperRepresentation) (string, error) - UpdateClientProtocolMapper(ctx context.Context, token, realm, idOfClient, mapperID string, mapper ProtocolMapperRepresentation) error - DeleteClientProtocolMapper(ctx context.Context, token, realm, idOfClient, mapperID string) error - - // *** Realm Roles *** - - CreateRealmRole(ctx context.Context, token, realm string, role Role) (string, error) - GetRealmRole(ctx context.Context, token, realm, roleName string) (*Role, error) - GetRealmRoles(ctx context.Context, accessToken, realm string, params GetRoleParams) ([]*Role, error) - GetRealmRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) - GetRealmRolesByUserID(ctx context.Context, accessToken, realm, userID string) ([]*Role, error) - GetRealmRolesByGroupID(ctx context.Context, accessToken, realm, groupID string) ([]*Role, error) - UpdateRealmRole(ctx context.Context, token, realm, roleName string, role Role) error - UpdateRealmRoleByID(ctx context.Context, token, realm, roleID string, role Role) error - DeleteRealmRole(ctx context.Context, token, realm, roleName string) error - AddRealmRoleToUser(ctx context.Context, token, realm, userID string, roles []Role) error - DeleteRealmRoleFromUser(ctx context.Context, token, realm, userID string, roles []Role) error - AddRealmRoleToGroup(ctx context.Context, token, realm, groupID string, roles []Role) error - DeleteRealmRoleFromGroup(ctx context.Context, token, realm, groupID string, roles []Role) error - AddRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error - DeleteRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error - GetCompositeRealmRoles(ctx context.Context, token, realm, roleName string) ([]*Role, error) - GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) - GetCompositeRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) - GetCompositeRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) - GetAvailableRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) - GetAvailableRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) - - // *** Client Roles *** - - AddClientRoleToUser(ctx context.Context, token, realm, idOfClient, userID string, roles []Role) error - AddClientRoleToGroup(ctx context.Context, token, realm, idOfClient, groupID string, roles []Role) error - DeleteClientRoleFromGroup(ctx context.Context, token, realm, idOfClient, groupID string, roles []Role) error - GetCompositeClientRolesByRoleID(ctx context.Context, token, realm, idOfClient, roleID string) ([]*Role, error) - GetClientRolesByUserID(ctx context.Context, token, realm, idOfClient, userID string) ([]*Role, error) - GetClientRolesByGroupID(ctx context.Context, token, realm, idOfClient, groupID string) ([]*Role, error) - GetCompositeClientRolesByUserID(ctx context.Context, token, realm, idOfClient, userID string) ([]*Role, error) - GetCompositeClientRolesByGroupID(ctx context.Context, token, realm, idOfClient, groupID string) ([]*Role, error) - GetAvailableClientRolesByUserID(ctx context.Context, token, realm, idOfClient, userID string) ([]*Role, error) - GetAvailableClientRolesByGroupID(ctx context.Context, token, realm, idOfClient, groupID string) ([]*Role, error) - - // *** Realm *** - - GetRealm(ctx context.Context, token, realm string) (*RealmRepresentation, error) - GetRealms(ctx context.Context, token string) ([]*RealmRepresentation, error) - CreateRealm(ctx context.Context, token string, realm RealmRepresentation) (string, error) - UpdateRealm(ctx context.Context, token string, realm RealmRepresentation) error - DeleteRealm(ctx context.Context, token, realm string) error - ClearRealmCache(ctx context.Context, token, realm string) error - ClearUserCache(ctx context.Context, token, realm string) error - ClearKeysCache(ctx context.Context, token, realm string) error - -GetClientUserSessions(ctx context.Context, token, realm, idOfClient string, params ...GetClientUserSessionsParams) ([]*UserSessionRepresentation, error) -GetClientOfflineSessions(ctx context.Context, token, realm, idOfClient string, params ...GetClientUserSessionsParams) ([]*UserSessionRepresentation, error) - GetUserSessions(ctx context.Context, token, realm, userID string) ([]*UserSessionRepresentation, error) - GetUserOfflineSessionsForClient(ctx context.Context, token, realm, userID, idOfClient string) ([]*UserSessionRepresentation, error) - - // *** Protection API *** - GetResource(ctx context.Context, token, realm, idOfClient, resourceID string) (*ResourceRepresentation, error) - GetResources(ctx context.Context, token, realm, idOfClient string, params GetResourceParams) ([]*ResourceRepresentation, error) - CreateResource(ctx context.Context, token, realm, idOfClient string, resource ResourceRepresentation) (*ResourceRepresentation, error) - UpdateResource(ctx context.Context, token, realm, idOfClient string, resource ResourceRepresentation) error - DeleteResource(ctx context.Context, token, realm, idOfClient, resourceID string) error - - GetResourceClient(ctx context.Context, token, realm, resourceID string) (*ResourceRepresentation, error) - GetResourcesClient(ctx context.Context, token, realm string, params GetResourceParams) ([]*ResourceRepresentation, error) - CreateResourceClient(ctx context.Context, token, realm string, resource ResourceRepresentation) (*ResourceRepresentation, error) - UpdateResourceClient(ctx context.Context, token, realm string, resource ResourceRepresentation) error - DeleteResourceClient(ctx context.Context, token, realm, resourceID string) error - - GetScope(ctx context.Context, token, realm, idOfClient, scopeID string) (*ScopeRepresentation, error) - GetScopes(ctx context.Context, token, realm, idOfClient string, params GetScopeParams) ([]*ScopeRepresentation, error) - CreateScope(ctx context.Context, token, realm, idOfClient string, scope ScopeRepresentation) (*ScopeRepresentation, error) - UpdateScope(ctx context.Context, token, realm, idOfClient string, resource ScopeRepresentation) error - DeleteScope(ctx context.Context, token, realm, idOfClient, scopeID string) error - - GetPolicy(ctx context.Context, token, realm, idOfClient, policyID string) (*PolicyRepresentation, error) - GetPolicies(ctx context.Context, token, realm, idOfClient string, params GetPolicyParams) ([]*PolicyRepresentation, error) - CreatePolicy(ctx context.Context, token, realm, idOfClient string, policy PolicyRepresentation) (*PolicyRepresentation, error) - UpdatePolicy(ctx context.Context, token, realm, idOfClient string, policy PolicyRepresentation) error - DeletePolicy(ctx context.Context, token, realm, idOfClient, policyID string) error - - GetResourcePolicy(ctx context.Context, token, realm, permissionID string) (*ResourcePolicyRepresentation, error) - GetResourcePolicies(ctx context.Context, token, realm string, params GetResourcePoliciesParams) ([]*ResourcePolicyRepresentation, error) - CreateResourcePolicy(ctx context.Context, token, realm, resourceID string, policy ResourcePolicyRepresentation) (*ResourcePolicyRepresentation, error) - UpdateResourcePolicy(ctx context.Context, token, realm, permissionID string, policy ResourcePolicyRepresentation) error - DeleteResourcePolicy(ctx context.Context, token, realm, permissionID string) error - - GetPermission(ctx context.Context, token, realm, idOfClient, permissionID string) (*PermissionRepresentation, error) - GetPermissions(ctx context.Context, token, realm, idOfClient string, params GetPermissionParams) ([]*PermissionRepresentation, error) - GetPermissionResources(ctx context.Context, token, realm, idOfClient, permissionID string) ([]*PermissionResource, error) - GetPermissionScopes(ctx context.Context, token, realm, idOfClient, permissionID string) ([]*PermissionScope, error) - GetDependentPermissions(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PermissionRepresentation, error) - CreatePermission(ctx context.Context, token, realm, idOfClient string, permission PermissionRepresentation) (*PermissionRepresentation, error) - UpdatePermission(ctx context.Context, token, realm, idOfClient string, permission PermissionRepresentation) error - DeletePermission(ctx context.Context, token, realm, idOfClient, permissionID string) error - - CreatePermissionTicket(ctx context.Context, token, realm string, permissions []CreatePermissionTicketParams) (*PermissionTicketResponseRepresentation, error) - GrantUserPermission(ctx context.Context, token, realm string, permission PermissionGrantParams) (*PermissionGrantResponseRepresentation, error) - UpdateUserPermission(ctx context.Context, token, realm string, permission PermissionGrantParams) (*PermissionGrantResponseRepresentation, error) - GetUserPermissions(ctx context.Context, token, realm string, params GetUserPermissionParams) ([]*PermissionGrantResponseRepresentation, error) - DeleteUserPermission(ctx context.Context, token, realm, ticketID string) error - - // *** Credentials API *** - - GetCredentialRegistrators(ctx context.Context, token, realm string) ([]string, error) - GetConfiguredUserStorageCredentialTypes(ctx context.Context, token, realm, userID string) ([]string, error) - GetCredentials(ctx context.Context, token, realm, UserID string) ([]*CredentialRepresentation, error) - DeleteCredentials(ctx context.Context, token, realm, UserID, CredentialID string) error - UpdateCredentialUserLabel(ctx context.Context, token, realm, userID, credentialID, userLabel string) error - DisableAllCredentialsByType(ctx context.Context, token, realm, userID string, types []string) error - MoveCredentialBehind(ctx context.Context, token, realm, userID, credentialID, newPreviousCredentialID string) error - MoveCredentialToFirst(ctx context.Context, token, realm, userID, credentialID string) error - -// *** Authentication Flows *** -GetAuthenticationFlows(ctx context.Context, token, realm string) ([]*AuthenticationFlowRepresentation, error) -GetAuthenticationFlow(ctx context.Context, token, realm string, authenticationFlowID string) (*AuthenticationFlowRepresentation, error) -CreateAuthenticationFlow(ctx context.Context, token, realm string, flow AuthenticationFlowRepresentation) error -UpdateAuthenticationFlow(ctx context.Context, token, realm string, flow AuthenticationFlowRepresentation, authenticationFlowID string) (*AuthenticationFlowRepresentation, error) -DeleteAuthenticationFlow(ctx context.Context, token, realm, flowID string) error - -// *** Identity Providers *** - - CreateIdentityProvider(ctx context.Context, token, realm string, providerRep IdentityProviderRepresentation) (string, error) - GetIdentityProvider(ctx context.Context, token, realm, alias string) (*IdentityProviderRepresentation, error) - GetIdentityProviders(ctx context.Context, token, realm string) ([]*IdentityProviderRepresentation, error) - UpdateIdentityProvider(ctx context.Context, token, realm, alias string, providerRep IdentityProviderRepresentation) error - DeleteIdentityProvider(ctx context.Context, token, realm, alias string) error - - CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) (string, error) - GetIdentityProviderMapper(ctx context.Context, token string, realm string, alias string, mapperID string) (*IdentityProviderMapper, error) - CreateUserFederatedIdentity(ctx context.Context, token, realm, userID, providerID string, federatedIdentityRep FederatedIdentityRepresentation) error - GetUserFederatedIdentities(ctx context.Context, token, realm, userID string) ([]*FederatedIdentityRepresentation, error) - DeleteUserFederatedIdentity(ctx context.Context, token, realm, userID, providerID string) error - - // *** Events API *** - GetEvents(ctx context.Context, token string, realm string, params GetEventsParams) ([]*EventRepresentation, error) +[GoCloakIface](gocloak_iface.go) holds all methods a client should fulfil. -} -``` ## Configure gocloak to skip TLS Insecure Verification diff --git a/client.go b/client.go index 210211a4..0cd8de5a 100644 --- a/client.go +++ b/client.go @@ -17,6 +17,7 @@ import ( "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/segmentio/ksuid" + "golang.org/x/mod/semver" "github.com/Nerzal/gocloak/v13/pkg/jwx" ) @@ -36,9 +37,13 @@ type GoCloak struct { logoutEndpoint string openIDConnect string attackDetection string + version string } } +// Verify struct implements interface +var _ GoCloakIface = &GoCloak{} + const ( adminClientID string = "admin-cli" urlSeparator string = "/" @@ -48,6 +53,53 @@ func makeURL(path ...string) string { return strings.Join(path, urlSeparator) } +// Compares the provided version against the current version of the Keycloak server. +// Current version is fetched from the serverinfo if not already set. +// +// Returns: +// +// -1 if the provided version is lower than the server version +// +// 0 if the provided version is equal to the server version +// +// 1 if the provided version is higher than the server version +func (g *GoCloak) compareVersions(v, token string, ctx context.Context) (int, error) { + curVersion := g.Config.version + if curVersion == "" { + curV, err := g.getServerVersion(ctx, token) + if err != nil { + return 0, err + } + + curVersion = curV + } + + curVersion = "v" + g.Config.version + if v[0] != 'v' { + v = "v" + v + } + + return semver.Compare(curVersion, v), nil +} + +// Get the server version from the serverinfo endpoint. +// If the version is already set, it will return the cached version. +// Otherwise, it will fetch the version from the serverinfo endpoint and cache it. +func (g *GoCloak) getServerVersion(ctx context.Context, token string) (string, error) { + if g.Config.version != "" { + return g.Config.version, nil + } + + serverInfo, err := g.GetServerInfo(ctx, token) + if err != nil { + return "", err + } + + g.Config.version = *(serverInfo.SystemInfo.Version) + + return g.Config.version, nil +} + // GetRequest returns a request for calling endpoints. func (g *GoCloak) GetRequest(ctx context.Context) *resty.Request { var err HTTPErrorResponse @@ -580,6 +632,19 @@ func (g *GoCloak) LoginClientTokenExchange(ctx context.Context, clientID, token, return g.GetToken(ctx, realm, tokenOptions) } +// DirectNakedImpersonationTokenExchange performs "Direct Naked Impersonation" +// See: https://www.keycloak.org/docs/latest/securing_apps/index.html#direct-naked-impersonation +func (g *GoCloak) DirectNakedImpersonationTokenExchange(ctx context.Context, clientID, clientSecret, realm, userID string) (*JWT, error) { + tokenOptions := TokenOptions{ + ClientID: &clientID, + ClientSecret: &clientSecret, + GrantType: StringP("urn:ietf:params:oauth:grant-type:token-exchange"), + RequestedTokenType: StringP("urn:ietf:params:oauth:token-type:refresh_token"), + RequestedSubject: StringP(userID), + } + return g.GetToken(ctx, realm, tokenOptions) +} + // LoginClientSignedJWT performs a login with client credentials and signed jwt claims func (g *GoCloak) LoginClientSignedJWT( ctx context.Context, @@ -712,7 +777,7 @@ func (g *GoCloak) ExecuteActionsEmail(ctx context.Context, token, realm string, // SendVerifyEmail sends a verification e-mail to a user. func (g *GoCloak) SendVerifyEmail(ctx context.Context, token, userID, realm string, params ...SendVerificationMailParams) error { - const errMessage = "could not execute actions email" + const errMessage = "failed to send verify email" queryParams := map[string]string{} if params != nil { @@ -1288,6 +1353,22 @@ func (g *GoCloak) GetClientScopeMappings(ctx context.Context, token, realm, idOf return result, nil } +// GetRealmRoleGroups returns groups associated with the realm role +func (g *GoCloak) GetRealmRoleGroups(ctx context.Context, token, roleName, realm string) ([]*Group, error) { + const errMessage = "could not get groups by realm roleName" + + var result []*Group + resp, err := g.GetRequestWithBearerAuth(ctx, token). + SetResult(&result). + Get(g.getAdminRealmURL(realm, "roles", roleName, "groups")) + + if err = checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return result, nil +} + // GetClientScopeMappingsRealmRoles returns realm-level roles associated with the client’s scope func (g *GoCloak) GetClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string) ([]*Role, error) { const errMessage = "could not get realm-level roles with the client’s scope" @@ -1700,6 +1781,28 @@ func (g *GoCloak) GetGroup(ctx context.Context, token, realm, groupID string) (* return &result, nil } +// GetChildGroups get child groups of group with id in realm +func (g *GoCloak) GetChildGroups(ctx context.Context, token, realm, groupID string, params GetChildGroupsParams) ([]*Group, error) { + const errMessage = "could not get child groups" + + var result []*Group + queryParams, err := GetQueryParams(params) + if err != nil { + return nil, errors.Wrap(err, errMessage) + } + + resp, err := g.GetRequestWithBearerAuth(ctx, token). + SetResult(&result). + SetQueryParams(queryParams). + Get(g.getAdminRealmURL(realm, "groups", groupID, "children")) + + if err := checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return result, nil +} + // GetGroupByPath get group with path in realm func (g *GoCloak) GetGroupByPath(ctx context.Context, token, realm, groupPath string) (*Group, error) { const errMessage = "could not get group" @@ -2368,7 +2471,9 @@ func (g *GoCloak) GetAvailableRealmRolesByGroupID(ctx context.Context, token, re // GetRealm returns top-level representation of the realm func (g *GoCloak) GetRealm(ctx context.Context, token, realm string) (*RealmRepresentation, error) { const errMessage = "could not get realm" - + if realm == "" { + return nil, errors.New("realm is empty") + } var result RealmRepresentation resp, err := g.GetRequestWithBearerAuth(ctx, token). SetResult(&result). @@ -3501,8 +3606,14 @@ func (g *GoCloak) GetPolicies(ctx context.Context, token, realm, idOfClient stri return nil, errors.Wrap(err, errMessage) } + compResult, err := g.compareVersions("20.0.0", token, ctx) + if err != nil { + return nil, err + } + shouldAddType := compResult != 1 + path := []string{"clients", idOfClient, "authz", "resource-server", "policy"} - if !NilOrEmpty(params.Type) { + if !NilOrEmpty(params.Type) && shouldAddType { path = append(path, *params.Type) } @@ -3527,11 +3638,23 @@ func (g *GoCloak) CreatePolicy(ctx context.Context, token, realm, idOfClient str return nil, errors.New("type of a policy required") } + compResult, err := g.compareVersions("20.0.0", token, ctx) + if err != nil { + return nil, err + } + shouldAddType := compResult != 1 + + path := []string{"clients", idOfClient, "authz", "resource-server", "policy"} + + if shouldAddType { + path = append(path, *policy.Type) + } + var result PolicyRepresentation resp, err := g.GetRequestWithBearerAuth(ctx, token). SetResult(&result). SetBody(policy). - Post(g.getAdminRealmURL(realm, "clients", idOfClient, "authz", "resource-server", "policy", *(policy.Type))) + Post(g.getAdminRealmURL(realm, path...)) if err := checkForError(resp, err, errMessage); err != nil { return nil, err @@ -3548,9 +3671,23 @@ func (g *GoCloak) UpdatePolicy(ctx context.Context, token, realm, idOfClient str return errors.New("ID of a policy required") } + compResult, err := g.compareVersions("20.0.0", token, ctx) + if err != nil { + return err + } + shouldAddType := compResult != 1 + + path := []string{"clients", idOfClient, "authz", "resource-server", "policy"} + + if shouldAddType { + path = append(path, *policy.Type) + } + + path = append(path, *(policy.ID)) + resp, err := g.GetRequestWithBearerAuth(ctx, token). SetBody(policy). - Put(g.getAdminRealmURL(realm, "clients", idOfClient, "authz", "resource-server", "policy", *(policy.Type), *(policy.ID))) + Put(g.getAdminRealmURL(realm, path...)) return checkForError(resp, err, errMessage) } @@ -3737,6 +3874,21 @@ func (g *GoCloak) GetPermissionResources(ctx context.Context, token, realm, idOf return result, nil } +// GetScopePermissions returns permissions associated with the client scope +func (g *GoCloak) GetScopePermissions(ctx context.Context, token, realm, idOfClient, idOfScope string) ([]*PolicyRepresentation, error) { + const errMessage = "could not get scope permissions" + + var result []*PolicyRepresentation + resp, err := g.GetRequestWithBearerAuth(ctx, token). + SetResult(&result). + Get(g.getAdminRealmURL(realm, "clients", idOfClient, "authz", "resource-server", "scope", idOfScope, "permissions")) + if err := checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return result, nil +} + // GetPermissionScopes returns a client's scopes configured for the given permission id func (g *GoCloak) GetPermissionScopes(ctx context.Context, token, realm, idOfClient, permissionID string) ([]*PermissionScope, error) { const errMessage = "could not get permission scopes" @@ -4187,6 +4339,23 @@ func (g *GoCloak) RegisterRequiredAction(ctx context.Context, token string, real return err } +// GetUnregisteredRequiredActions gets a list of unregistered required actions for a given realm +func (g *GoCloak) GetUnregisteredRequiredActions(ctx context.Context, token string, realm string) ([]*UnregisteredRequiredActionProviderRepresentation, error) { + const errMessage = "could not get unregistered required actions" + + var result []*UnregisteredRequiredActionProviderRepresentation + + resp, err := g.GetRequestWithBearerAuth(ctx, token). + SetResult(&result). + Get(g.getAdminRealmURL(realm, "authentication", "unregistered-required-actions")) + + if err := checkForError(resp, err, errMessage); err != nil { + return nil, err + } + + return result, nil +} + // GetRequiredActions gets a list of required actions for a given realm func (g *GoCloak) GetRequiredActions(ctx context.Context, token string, realm string) ([]*RequiredActionProviderRepresentation, error) { const errMessage = "could not get required actions" diff --git a/client_test.go b/client_test.go index 9f3fc6d4..749a1681 100644 --- a/client_test.go +++ b/client_test.go @@ -74,7 +74,7 @@ func GetConfig(t testing.TB) *Config { err := configFile.Close() require.NoError(t, err, "cannot close config file") }() - data, err := ioutil.ReadAll(configFile) + data, err := io.ReadAll(configFile) require.NoError(t, err, "cannot read config.json") config = &Config{} err = json.Unmarshal(data, config) @@ -92,7 +92,7 @@ func GetConfig(t testing.TB) *Config { return config } -func GetClientToken(t *testing.T, client *gocloak.GoCloak) *gocloak.JWT { +func GetClientToken(t *testing.T, client gocloak.GoCloakIface) *gocloak.JWT { cfg := GetConfig(t) token, err := client.LoginClient( context.Background(), @@ -103,7 +103,7 @@ func GetClientToken(t *testing.T, client *gocloak.GoCloak) *gocloak.JWT { return token } -func GetUserToken(t *testing.T, client *gocloak.GoCloak) *gocloak.JWT { +func GetUserToken(t *testing.T, client gocloak.GoCloakIface) *gocloak.JWT { SetUpTestUser(t, client) cfg := GetConfig(t) token, err := client.Login( @@ -117,7 +117,7 @@ func GetUserToken(t *testing.T, client *gocloak.GoCloak) *gocloak.JWT { return token } -func GetAdminToken(t testing.TB, client *gocloak.GoCloak) *gocloak.JWT { +func GetAdminToken(t testing.TB, client gocloak.GoCloakIface) *gocloak.JWT { cfg := GetConfig(t) token, err := client.LoginAdmin( context.Background(), @@ -140,7 +140,7 @@ func GetRandomNameP(name string) *string { return &r } -func GetClientByClientID(t *testing.T, client *gocloak.GoCloak, clientID string) *gocloak.Client { +func GetClientByClientID(t *testing.T, client gocloak.GoCloakIface, clientID string) *gocloak.Client { cfg := GetConfig(t) token := GetAdminToken(t, client) clients, err := client.GetClients( @@ -163,7 +163,7 @@ func GetClientByClientID(t *testing.T, client *gocloak.GoCloak, clientID string) return nil } -func CreateGroup(t testing.TB, client *gocloak.GoCloak) (func(), string) { +func CreateGroup(t testing.TB, client gocloak.GoCloakIface) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) group := gocloak.Group{ @@ -194,7 +194,7 @@ func CreateGroup(t testing.TB, client *gocloak.GoCloak) (func(), string) { return tearDown, groupID } -func CreateResource(t *testing.T, client *gocloak.GoCloak, idOfClient string) (func(), string) { +func CreateResource(t *testing.T, client gocloak.GoCloakIface, idOfClient string) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) resource := gocloak.ResourceRepresentation{ @@ -233,7 +233,7 @@ func CreateResource(t *testing.T, client *gocloak.GoCloak, idOfClient string) (f return tearDown, *createdResource.ID } -func CreateResourceClientWithScopes(t *testing.T, client *gocloak.GoCloak) (func(), string) { +func CreateResourceClientWithScopes(t *testing.T, client gocloak.GoCloakIface) (func(), string) { cfg := GetConfig(t) token := GetClientToken(t, client) resource := gocloak.ResourceRepresentation{ @@ -277,7 +277,7 @@ func CreateResourceClientWithScopes(t *testing.T, client *gocloak.GoCloak) (func return tearDown, *createdResource.ID } -func CreateResourceClient(t *testing.T, client *gocloak.GoCloak) (func(), string) { +func CreateResourceClient(t *testing.T, client gocloak.GoCloakIface) (func(), string) { cfg := GetConfig(t) token := GetClientToken(t, client) resource := gocloak.ResourceRepresentation{ @@ -314,7 +314,7 @@ func CreateResourceClient(t *testing.T, client *gocloak.GoCloak) (func(), string return tearDown, *createdResource.ID } -func CreateScope(t *testing.T, client *gocloak.GoCloak, idOfClient string) (func(), string) { +func CreateScope(t *testing.T, client gocloak.GoCloakIface, idOfClient string) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) scope := gocloak.ScopeRepresentation{ @@ -343,7 +343,7 @@ func CreateScope(t *testing.T, client *gocloak.GoCloak, idOfClient string) (func return tearDown, *createdScope.ID } -func CreatePolicy(t *testing.T, client *gocloak.GoCloak, idOfClient string, policy gocloak.PolicyRepresentation) (func(), string) { +func CreatePolicy(t *testing.T, client gocloak.GoCloakIface, idOfClient string, policy gocloak.PolicyRepresentation) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) createdPolicy, err := client.CreatePolicy( @@ -368,7 +368,7 @@ func CreatePolicy(t *testing.T, client *gocloak.GoCloak, idOfClient string, poli return tearDown, *createdPolicy.ID } -func CreatePermission(t *testing.T, client *gocloak.GoCloak, idOfClient string, permission gocloak.PermissionRepresentation) (func(), string) { +func CreatePermission(t *testing.T, client gocloak.GoCloakIface, idOfClient string, permission gocloak.PermissionRepresentation) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) createdPermission, err := client.CreatePermission( @@ -392,7 +392,7 @@ func CreatePermission(t *testing.T, client *gocloak.GoCloak, idOfClient string, return tearDown, *createdPermission.ID } -func CreateClient(t *testing.T, client *gocloak.GoCloak, newClient *gocloak.Client) (func(), string) { +func CreateClient(t *testing.T, client gocloak.GoCloakIface, newClient *gocloak.Client) (func(), string) { if newClient == nil { newClient = &gocloak.Client{ ClientID: GetRandomNameP("ClientID"), @@ -420,7 +420,7 @@ func CreateClient(t *testing.T, client *gocloak.GoCloak, newClient *gocloak.Clie return tearDown, createdID } -func SetUpTestUser(t testing.TB, client *gocloak.GoCloak) { +func SetUpTestUser(t testing.TB, client gocloak.GoCloakIface) { setupOnce.Do(func() { cfg := GetConfig(t) token := GetAdminToken(t, client) @@ -495,7 +495,7 @@ func (w *RestyLogWriter) write(format string, v ...interface{}) { func NewClientWithDebug(t testing.TB) *gocloak.GoCloak { cfg := GetConfig(t) client := gocloak.NewClient(cfg.HostName) - cond := func(resp *resty.Response, err error) bool { + cond := func(resp *resty.Response, _ error) bool { if resp != nil && resp.IsError() { if e, ok := resp.Error().(*gocloak.HTTPErrorResponse); ok { msg := e.String() @@ -536,7 +536,7 @@ func NewClientWithDebug(t testing.TB) *gocloak.GoCloak { // skipN = number of requests to be executed and not failed by this function func FailRequest(client *gocloak.GoCloak, err error, failN, skipN int) *gocloak.GoCloak { client.RestyClient().OnBeforeRequest( - func(c *resty.Client, r *resty.Request) error { + func(_ *resty.Client, r *resty.Request) error { if skipN > 0 { skipN-- return nil @@ -554,7 +554,7 @@ func FailRequest(client *gocloak.GoCloak, err error, failN, skipN int) *gocloak. return client } -func ClearRealmCache(t testing.TB, client *gocloak.GoCloak, realm ...string) { +func ClearRealmCache(t testing.TB, client gocloak.GoCloakIface, realm ...string) { cfg := GetConfig(t) token := GetAdminToken(t, client) if len(realm) == 0 { @@ -1345,7 +1345,7 @@ func Test_GroupPermissions(t *testing.T) { } } -func CreateClientRole(t *testing.T, client *gocloak.GoCloak) (func(), string) { +func CreateClientRole(t *testing.T, client gocloak.GoCloakIface) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) @@ -1456,14 +1456,15 @@ func Test_GetClientRole(t *testing.T) { require.Nil(t, role) } -func CreateClientScope(t *testing.T, client *gocloak.GoCloak, scope *gocloak.ClientScope) (func(), string) { +func CreateClientScope(t *testing.T, client gocloak.GoCloakIface, scope *gocloak.ClientScope) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) if scope == nil { scope = &gocloak.ClientScope{ - ID: GetRandomNameP("client-scope-id-"), - Name: GetRandomNameP("client-scope-name-"), + ID: GetRandomNameP("client-scope-id-"), + Name: GetRandomNameP("client-scope-name-"), + Protocol: gocloak.StringP("openid-connect"), } } @@ -1474,10 +1475,10 @@ func CreateClientScope(t *testing.T, client *gocloak.GoCloak, scope *gocloak.Cli cfg.GoCloak.Realm, *scope, ) + require.NoError(t, err, "CreateClientScope failed") if !gocloak.NilOrEmpty(scope.ID) { require.Equal(t, clientScopeID, *scope.ID) } - require.NoError(t, err, "CreateClientScope failed") tearDown := func() { err := client.DeleteClientScope( context.Background(), @@ -1497,7 +1498,7 @@ func Test_CreateClientScope_DeleteClientScope(t *testing.T) { tearDown() } -func CreateUpdateClientScopeProtocolMapper(t *testing.T, client *gocloak.GoCloak, scopeID string, protocolMapper *gocloak.ProtocolMappers) (func(), string) { +func CreateUpdateClientScopeProtocolMapper(t *testing.T, client gocloak.GoCloakIface, scopeID string, protocolMapper *gocloak.ProtocolMappers) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) @@ -1790,7 +1791,7 @@ func Test_GetClientScopeProtocolMappers(t *testing.T) { require.NotNil(t, protocolMappers) } -func CreateClientScopeMappingsRealmRoles(t *testing.T, client *gocloak.GoCloak, idOfClient string, roles []gocloak.Role) func() { +func CreateClientScopeMappingsRealmRoles(t *testing.T, client gocloak.GoCloakIface, idOfClient string, roles []gocloak.Role) func() { token := GetAdminToken(t, client) cfg := GetConfig(t) @@ -1817,7 +1818,7 @@ func CreateClientScopeMappingsRealmRoles(t *testing.T, client *gocloak.GoCloak, return tearDown } -func CreateClientScopeMappingsClientRoles(t *testing.T, client *gocloak.GoCloak, idOfClient, clients string, roles []gocloak.Role) func() { +func CreateClientScopeMappingsClientRoles(t *testing.T, client gocloak.GoCloakIface, idOfClient, clients string, roles []gocloak.Role) func() { token := GetAdminToken(t, client) cfg := GetConfig(t) @@ -2000,7 +2001,7 @@ func Test_ClientScopeMappingsRealmRoles(t *testing.T) { } func CreateClientScopesMappingsClientRoles( - t *testing.T, client *gocloak.GoCloak, scopeID, idOfClient string, roles []gocloak.Role, + t *testing.T, client gocloak.GoCloakIface, scopeID, idOfClient string, roles []gocloak.Role, ) func() { token := GetAdminToken(t, client) cfg := GetConfig(t) @@ -2479,6 +2480,40 @@ func Test_GetGroupFull(t *testing.T) { require.True(t, ok, "UserAttributeContains") } +func Test_GetChildGroups(t *testing.T) { + t.Parallel() + cfg := GetConfig(t) + client := NewClientWithDebug(t) + token := GetAdminToken(t, client) + + tearDown, groupID := CreateGroup(t, client) + defer tearDown() + + childGroupIDs := []string{} + for i := 0; i < 3; i++ { + childGroupID, err := client.CreateChildGroup(context.Background(), + token.AccessToken, + cfg.GoCloak.Realm, + groupID, + gocloak.Group{ + Name: GetRandomNameP("Group"), + }, + ) + require.NoError(t, err, "CreateChildGroup failed") + childGroupIDs = append(childGroupIDs, childGroupID) + } + + childGroups, err := client.GetChildGroups( + context.Background(), + token.AccessToken, + cfg.GoCloak.Realm, + groupID, + gocloak.GetChildGroupsParams{}, + ) + require.NoError(t, err, "GetChildGroups failed") + require.Len(t, childGroups, len(childGroupIDs)) +} + func Test_GetGroupMembers(t *testing.T) { t.Parallel() cfg := GetConfig(t) @@ -2659,7 +2694,7 @@ func Test_SendVerifyEmail(t *testing.T) { if err.Error() == "500 Internal Server Error: Failed to send execute actions email" { return } - require.NoError(t, err, "ExecuteActionsEmail failed") + require.NoError(t, err, "SendVerifyEmail failed") } } @@ -2775,7 +2810,7 @@ func Test_GetRealms(t *testing.T) { // Realm // ----------- -func CreateRealm(t *testing.T, client *gocloak.GoCloak) (func(), string) { +func CreateRealm(t *testing.T, client gocloak.GoCloakIface) (func(), string) { token := GetAdminToken(t, client) realmName := GetRandomName("Realm") @@ -2852,7 +2887,7 @@ func Test_ClearRealmCache(t *testing.T) { // Realm Roles // ----------- -func CreateRealmRole(t *testing.T, client *gocloak.GoCloak) (func(), string) { +func CreateRealmRole(t *testing.T, client gocloak.GoCloakIface) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) @@ -3198,7 +3233,7 @@ func Test_AddRealmRoleComposite_DeleteRealmRoleComposite(t *testing.T) { // Users // ----- -func CreateUser(t *testing.T, client *gocloak.GoCloak) (func(), string) { +func CreateUser(t *testing.T, client gocloak.GoCloakIface) (func(), string) { cfg := GetConfig(t) token := GetAdminToken(t, client) @@ -3789,8 +3824,9 @@ func Test_GetClientUserSessions(t *testing.T) { ) require.NoError(t, err, "GetClientUserSessions failed") require.NotEmpty(t, allSessions, "GetClientUserSessions returned an empty list") + require.Equal(t, allSessionsWithoutParams, allSessions, - "GetClientUserSessions with and without params are not the same") + "GetClientUserSessions with and without params are the same") sessions, err := client.GetClientUserSessions( context.Background(), @@ -3969,7 +4005,7 @@ func Test_GetClientOfflineSessions(t *testing.T) { require.NoError(t, err, "GetClientOfflineSessions failed") require.NotEmpty(t, sessions, "GetClientOfflineSessions returned an empty list") require.Equal(t, sessions, sessionsWithoutParams, - "GetClientOfflineSessions with and without params are not the same") + "GetClientOfflineSessions with and without params are the same") } func Test_ClientSecret(t *testing.T) { @@ -5800,8 +5836,8 @@ func Test_GetAuthorizationPolicyScopes(t *testing.T) { require.Equal(t, *scopes[0].ID, scopeID) defer func() { - scope() policy() + scope() }() } @@ -6305,7 +6341,7 @@ func Test_CreatePermissionTicket(t *testing.T) { require.NoError(t, err, "CreatePermissionTicket failed") t.Logf("Created PermissionTicket: %+v", *(ticket.Ticket)) - pt, err := jwt.ParseWithClaims(*(ticket.Ticket), &gocloak.PermissionTicketRepresentation{}, func(token *jwt.Token) (interface{}, error) { + pt, err := jwt.ParseWithClaims(*(ticket.Ticket), &gocloak.PermissionTicketRepresentation{}, func(_ *jwt.Token) (interface{}, error) { return []byte(""), nil }) @@ -6922,16 +6958,22 @@ func TestGocloak_CreateAndGetRequiredAction(t *testing.T) { cfg := GetConfig(t) client := NewClientWithDebug(t) token := GetAdminToken(t, client) + + // need to get unregistered required actions first + // refer to test suit of Keycloak for more details + // https://github.com/keycloak/keycloak/blob/main/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/RequiredActionsTest.java#L93 + unregisteredRequiredActions, err := client.GetUnregisteredRequiredActions(context.Background(), token.AccessToken, cfg.GoCloak.Realm) + require.NoError(t, err, "Failed to get required actions") + require.NotEmpty(t, unregisteredRequiredActions, "Required actions must not be empty") + require.NotNil(t, unregisteredRequiredActions[0].Name, "Required action name must not be nil") + require.NotNil(t, unregisteredRequiredActions[0].ProviderID, "Required action alias must not be nil") + requiredAction := gocloak.RequiredActionProviderRepresentation{ - Alias: gocloak.StringP("VERIFY_EMAIL_NEW"), - Config: nil, - DefaultAction: gocloak.BoolP(false), - Enabled: gocloak.BoolP(true), - Name: gocloak.StringP("Verify Email new"), - Priority: gocloak.Int32P(50), - ProviderID: gocloak.StringP("VERIFY_EMAIL_NEW"), + Alias: unregisteredRequiredActions[0].ProviderID, + Name: unregisteredRequiredActions[0].Name, + ProviderID: unregisteredRequiredActions[0].ProviderID, } - err := client.RegisterRequiredAction(context.Background(), token.AccessToken, cfg.GoCloak.Realm, requiredAction) + err = client.RegisterRequiredAction(context.Background(), token.AccessToken, cfg.GoCloak.Realm, requiredAction) require.NoError(t, err, "Failed to register required action") ra, err := client.GetRequiredAction(context.Background(), token.AccessToken, cfg.GoCloak.Realm, *requiredAction.Alias) @@ -6997,7 +7039,7 @@ func TestGocloak_UpdateRequiredAction(t *testing.T) { require.NoError(t, err, "Failed to update required action") } -func CreateComponent(t *testing.T, client *gocloak.GoCloak) (func(), *gocloak.Component) { +func CreateComponent(t *testing.T, client gocloak.GoCloakIface) (func(), *gocloak.Component) { newComponent := &gocloak.Component{ Name: GetRandomNameP("CreateComponent"), ProviderID: gocloak.StringP("rsa-generated"), diff --git a/docker-compose.yml b/docker-compose.yml index b422ddbb..c467aad6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,4 @@ -version: '3' - +version: "3" services: keycloak: build: . @@ -12,12 +11,12 @@ services: KC_HEALTH_ENABLED: "true" ports: - "8080:8080" + - "9000:9000" healthcheck: - test: curl --fail --silent http://localhost:8080/health/ready 2>&1 || exit 1 + test: curl --fail --silent http://localhost:9000/health/ready 2>&1 || exit 1 interval: 10s timeout: 10s retries: 5 volumes: - - ./testdata/gocloak-realm.json:/opt/keycloak/data/import/gocloak-realm.json - entrypoint: ["/opt/keycloak/bin/kc.sh", "start-dev --features=preview --import-realm"] - \ No newline at end of file + - ./testdata/gocloak-realm.json:/opt/keycloak/data/import/gocloak-realm.json + entrypoint: ["/opt/keycloak/bin/kc.sh", "start-dev", "--features=preview", "--import-realm"] diff --git a/go.mod b/go.mod index cde01123..546c60c1 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,21 @@ module github.com/Nerzal/gocloak/v13 -go 1.18 +go 1.23.0 require ( - github.com/go-resty/resty/v2 v2.7.0 - github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/go-resty/resty/v2 v2.16.5 + github.com/golang-jwt/jwt/v5 v5.2.2 github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 github.com/segmentio/ksuid v1.0.4 - github.com/stretchr/testify v1.8.2 - golang.org/x/crypto v0.17.0 + github.com/stretchr/testify v1.10.0 + golang.org/x/crypto v0.36.0 + golang.org/x/mod v0.24.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/net v0.37.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 86853e21..81c71c10 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= +github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -14,25 +14,20 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gocloak_iface.go b/gocloak_iface.go new file mode 100644 index 00000000..b8c53121 --- /dev/null +++ b/gocloak_iface.go @@ -0,0 +1,585 @@ +// Code generated by ifacemaker; DO NOT EDIT. + +package gocloak + +import ( + "context" + "io" + + "github.com/go-resty/resty/v2" + "github.com/golang-jwt/jwt/v5" +) + +// GoCloakIface ... +type GoCloakIface interface { + // GetRequest returns a request for calling endpoints. + GetRequest(ctx context.Context) *resty.Request + // GetRequestWithBearerAuthNoCache returns a JSON base request configured with an auth token and no-cache header. + GetRequestWithBearerAuthNoCache(ctx context.Context, token string) *resty.Request + // GetRequestWithBearerAuth returns a JSON base request configured with an auth token. + GetRequestWithBearerAuth(ctx context.Context, token string) *resty.Request + // GetRequestWithBearerAuthXMLHeader returns an XML base request configured with an auth token. + GetRequestWithBearerAuthXMLHeader(ctx context.Context, token string) *resty.Request + // GetRequestWithBasicAuth returns a form data base request configured with basic auth. + GetRequestWithBasicAuth(ctx context.Context, clientID, clientSecret string) *resty.Request + // RestyClient returns the internal resty g. + // This can be used to configure the g. + RestyClient() *resty.Client + // SetRestyClient overwrites the internal resty g. + SetRestyClient(restyClient *resty.Client) + // GetServerInfo fetches the server info. + GetServerInfo(ctx context.Context, accessToken string) (*ServerInfoRepresentation, error) + // GetUserInfo calls the UserInfo endpoint + GetUserInfo(ctx context.Context, accessToken, realm string) (*UserInfo, error) + // GetRawUserInfo calls the UserInfo endpoint and returns a raw json object + GetRawUserInfo(ctx context.Context, accessToken, realm string) (map[string]interface{}, error) + // GetCerts fetches certificates for the given realm from the public /open-id-connect/certs endpoint + GetCerts(ctx context.Context, realm string) (*CertResponse, error) + // GetIssuer gets the issuer of the given realm + GetIssuer(ctx context.Context, realm string) (*IssuerResponse, error) + // RetrospectToken calls the openid-connect introspect endpoint + RetrospectToken(ctx context.Context, accessToken, clientID, clientSecret, realm string) (*IntroSpectTokenResult, error) + // DecodeAccessToken decodes the accessToken + DecodeAccessToken(ctx context.Context, accessToken, realm string) (*jwt.Token, *jwt.MapClaims, error) + // DecodeAccessTokenCustomClaims decodes the accessToken and writes claims into the given claims + DecodeAccessTokenCustomClaims(ctx context.Context, accessToken, realm string, claims jwt.Claims) (*jwt.Token, error) + // GetToken uses TokenOptions to fetch a token. + GetToken(ctx context.Context, realm string, options TokenOptions) (*JWT, error) + // GetRequestingPartyToken returns a requesting party token with permissions granted by the server + GetRequestingPartyToken(ctx context.Context, token, realm string, options RequestingPartyTokenOptions) (*JWT, error) + // GetRequestingPartyPermissions returns a requesting party permissions granted by the server + GetRequestingPartyPermissions(ctx context.Context, token, realm string, options RequestingPartyTokenOptions) (*[]RequestingPartyPermission, error) + // GetRequestingPartyPermissionDecision returns a requesting party permission decision granted by the server + GetRequestingPartyPermissionDecision(ctx context.Context, token, realm string, options RequestingPartyTokenOptions) (*RequestingPartyPermissionDecision, error) + // RefreshToken refreshes the given token. + // May return a *APIError with further details about the issue. + RefreshToken(ctx context.Context, refreshToken, clientID, clientSecret, realm string) (*JWT, error) + // LoginAdmin performs a login with Admin client + LoginAdmin(ctx context.Context, username, password, realm string) (*JWT, error) + // LoginClient performs a login with client credentials + LoginClient(ctx context.Context, clientID, clientSecret, realm string, scopes ...string) (*JWT, error) + // LoginClientTokenExchange will exchange the presented token for a user's token + // Requires Token-Exchange is enabled: https://www.keycloak.org/docs/latest/securing_apps/index.html#_token-exchange + LoginClientTokenExchange(ctx context.Context, clientID, token, clientSecret, realm, targetClient, userID string) (*JWT, error) + // DirectNakedImpersonationTokenExchange performs "Direct Naked Impersonation" + // See: https://www.keycloak.org/docs/latest/securing_apps/index.html#direct-naked-impersonation + DirectNakedImpersonationTokenExchange(ctx context.Context, clientID, clientSecret, realm, userID string) (*JWT, error) + // LoginClientSignedJWT performs a login with client credentials and signed jwt claims + LoginClientSignedJWT(ctx context.Context, clientID, realm string, key interface{}, signedMethod jwt.SigningMethod, expiresAt *jwt.NumericDate) (*JWT, error) + // Login performs a login with user credentials and a client + Login(ctx context.Context, clientID, clientSecret, realm, username, password string) (*JWT, error) + // LoginOtp performs a login with user credentials and otp token + LoginOtp(ctx context.Context, clientID, clientSecret, realm, username, password, totp string) (*JWT, error) + // Logout logs out users with refresh token + Logout(ctx context.Context, clientID, clientSecret, realm, refreshToken string) error + // LogoutPublicClient performs a logout using a public client and the accessToken. + LogoutPublicClient(ctx context.Context, clientID, realm, accessToken, refreshToken string) error + // LogoutAllSessions logs out all sessions of a user given an id. + LogoutAllSessions(ctx context.Context, accessToken, realm, userID string) error + // RevokeUserConsents revokes the given user consent. + RevokeUserConsents(ctx context.Context, accessToken, realm, userID, clientID string) error + // LogoutUserSession logs out a single sessions of a user given a session id + LogoutUserSession(ctx context.Context, accessToken, realm, session string) error + // ExecuteActionsEmail executes an actions email + ExecuteActionsEmail(ctx context.Context, token, realm string, params ExecuteActionsEmail) error + // SendVerifyEmail sends a verification e-mail to a user. + SendVerifyEmail(ctx context.Context, token, userID, realm string, params ...SendVerificationMailParams) error + // CreateGroup creates a new group. + CreateGroup(ctx context.Context, token, realm string, group Group) (string, error) + // CreateChildGroup creates a new child group + CreateChildGroup(ctx context.Context, token, realm, groupID string, group Group) (string, error) + // CreateComponent creates the given component. + CreateComponent(ctx context.Context, token, realm string, component Component) (string, error) + // CreateClient creates the given g. + CreateClient(ctx context.Context, accessToken, realm string, newClient Client) (string, error) + // CreateClientRepresentation creates a new client representation + CreateClientRepresentation(ctx context.Context, token, realm string, newClient Client) (*Client, error) + // CreateClientRole creates a new role for a client + CreateClientRole(ctx context.Context, token, realm, idOfClient string, role Role) (string, error) + // CreateClientScope creates a new client scope + CreateClientScope(ctx context.Context, token, realm string, scope ClientScope) (string, error) + // CreateClientScopeProtocolMapper creates a new protocolMapper under the given client scope + CreateClientScopeProtocolMapper(ctx context.Context, token, realm, scopeID string, protocolMapper ProtocolMappers) (string, error) + // UpdateGroup updates the given group. + UpdateGroup(ctx context.Context, token, realm string, updatedGroup Group) error + // UpdateGroupManagementPermissions updates the given group management permissions + UpdateGroupManagementPermissions(ctx context.Context, accessToken, realm string, idOfGroup string, managementPermissions ManagementPermissionRepresentation) (*ManagementPermissionRepresentation, error) + // UpdateClient updates the given Client + UpdateClient(ctx context.Context, token, realm string, updatedClient Client) error + // UpdateClientRepresentation updates the given client representation + UpdateClientRepresentation(ctx context.Context, accessToken, realm string, updatedClient Client) (*Client, error) + // UpdateClientManagementPermissions updates the given client management permissions + UpdateClientManagementPermissions(ctx context.Context, accessToken, realm string, idOfClient string, managementPermissions ManagementPermissionRepresentation) (*ManagementPermissionRepresentation, error) + // UpdateRole updates the given role. + UpdateRole(ctx context.Context, token, realm, idOfClient string, role Role) error + // UpdateClientScope updates the given client scope. + UpdateClientScope(ctx context.Context, token, realm string, scope ClientScope) error + // UpdateClientScopeProtocolMapper updates the given protocol mapper for a client scope + UpdateClientScopeProtocolMapper(ctx context.Context, token, realm, scopeID string, protocolMapper ProtocolMappers) error + // DeleteGroup deletes the group with the given groupID. + DeleteGroup(ctx context.Context, token, realm, groupID string) error + // DeleteClient deletes a given client + DeleteClient(ctx context.Context, token, realm, idOfClient string) error + // DeleteComponent deletes the component with the given id. + DeleteComponent(ctx context.Context, token, realm, componentID string) error + // DeleteClientRepresentation deletes a given client representation. + DeleteClientRepresentation(ctx context.Context, accessToken, realm, clientID string) error + // DeleteClientRole deletes a given role. + DeleteClientRole(ctx context.Context, token, realm, idOfClient, roleName string) error + // DeleteClientScope deletes the scope with the given id. + DeleteClientScope(ctx context.Context, token, realm, scopeID string) error + // DeleteClientScopeProtocolMapper deletes the given protocol mapper from the client scope + DeleteClientScopeProtocolMapper(ctx context.Context, token, realm, scopeID, protocolMapperID string) error + // GetClient returns a client + GetClient(ctx context.Context, token, realm, idOfClient string) (*Client, error) + // GetClientRepresentation returns a client representation + GetClientRepresentation(ctx context.Context, accessToken, realm, clientID string) (*Client, error) + // GetAdapterConfiguration returns a adapter configuration + GetAdapterConfiguration(ctx context.Context, accessToken, realm, clientID string) (*AdapterConfiguration, error) + // GetClientsDefaultScopes returns a list of the client's default scopes + GetClientsDefaultScopes(ctx context.Context, token, realm, idOfClient string) ([]*ClientScope, error) + // AddDefaultScopeToClient adds a client scope to the list of client's default scopes + AddDefaultScopeToClient(ctx context.Context, token, realm, idOfClient, scopeID string) error + // RemoveDefaultScopeFromClient removes a client scope from the list of client's default scopes + RemoveDefaultScopeFromClient(ctx context.Context, token, realm, idOfClient, scopeID string) error + // GetClientsOptionalScopes returns a list of the client's optional scopes + GetClientsOptionalScopes(ctx context.Context, token, realm, idOfClient string) ([]*ClientScope, error) + // AddOptionalScopeToClient adds a client scope to the list of client's optional scopes + AddOptionalScopeToClient(ctx context.Context, token, realm, idOfClient, scopeID string) error + // RemoveOptionalScopeFromClient deletes a client scope from the list of client's optional scopes + RemoveOptionalScopeFromClient(ctx context.Context, token, realm, idOfClient, scopeID string) error + // GetDefaultOptionalClientScopes returns a list of default realm optional scopes + GetDefaultOptionalClientScopes(ctx context.Context, token, realm string) ([]*ClientScope, error) + // GetDefaultDefaultClientScopes returns a list of default realm default scopes + GetDefaultDefaultClientScopes(ctx context.Context, token, realm string) ([]*ClientScope, error) + // GetClientScope returns a clientscope + GetClientScope(ctx context.Context, token, realm, scopeID string) (*ClientScope, error) + // GetClientScopes returns all client scopes + GetClientScopes(ctx context.Context, token, realm string) ([]*ClientScope, error) + // GetClientScopeProtocolMappers returns all protocol mappers of a client scope + GetClientScopeProtocolMappers(ctx context.Context, token, realm, scopeID string) ([]*ProtocolMappers, error) + // GetClientScopeProtocolMapper returns a protocol mapper of a client scope + GetClientScopeProtocolMapper(ctx context.Context, token, realm, scopeID, protocolMapperID string) (*ProtocolMappers, error) + // GetClientScopeMappings returns all scope mappings for the client + GetClientScopeMappings(ctx context.Context, token, realm, idOfClient string) (*MappingsRepresentation, error) + // GetRealmRoleGroups returns groups associated with the realm role + GetRealmRoleGroups(ctx context.Context, token, roleName, realm string) ([]*Group, error) + // GetClientScopeMappingsRealmRoles returns realm-level roles associated with the client’s scope + GetClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string) ([]*Role, error) + // GetClientScopeMappingsRealmRolesAvailable returns realm-level roles that are available to attach to this client’s scope + GetClientScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, idOfClient string) ([]*Role, error) + // CreateClientScopeMappingsRealmRoles create realm-level roles to the client’s scope + CreateClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string, roles []Role) error + // DeleteClientScopeMappingsRealmRoles deletes realm-level roles from the client’s scope + DeleteClientScopeMappingsRealmRoles(ctx context.Context, token, realm, idOfClient string, roles []Role) error + // GetClientScopeMappingsClientRoles returns roles associated with a client’s scope + GetClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error) + // GetClientScopeMappingsClientRolesAvailable returns available roles associated with a client’s scope + GetClientScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string) ([]*Role, error) + // CreateClientScopeMappingsClientRoles creates client-level roles from the client’s scope + CreateClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error + // DeleteClientScopeMappingsClientRoles deletes client-level roles from the client’s scope + DeleteClientScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClient, idOfSelectedClient string, roles []Role) error + // GetClientSecret returns a client's secret + GetClientSecret(ctx context.Context, token, realm, idOfClient string) (*CredentialRepresentation, error) + // GetClientServiceAccount retrieves the service account "user" for a client if enabled + GetClientServiceAccount(ctx context.Context, token, realm, idOfClient string) (*User, error) + // RegenerateClientSecret triggers the creation of the new client secret. + RegenerateClientSecret(ctx context.Context, token, realm, idOfClient string) (*CredentialRepresentation, error) + // GetClientOfflineSessions returns offline sessions associated with the client + GetClientOfflineSessions(ctx context.Context, token, realm, idOfClient string, params ...GetClientUserSessionsParams) ([]*UserSessionRepresentation, error) + // GetClientUserSessions returns user sessions associated with the client + GetClientUserSessions(ctx context.Context, token, realm, idOfClient string, params ...GetClientUserSessionsParams) ([]*UserSessionRepresentation, error) + // CreateClientProtocolMapper creates a protocol mapper in client scope + CreateClientProtocolMapper(ctx context.Context, token, realm, idOfClient string, mapper ProtocolMapperRepresentation) (string, error) + // UpdateClientProtocolMapper updates a protocol mapper in client scope + UpdateClientProtocolMapper(ctx context.Context, token, realm, idOfClient, mapperID string, mapper ProtocolMapperRepresentation) error + // DeleteClientProtocolMapper deletes a protocol mapper in client scope + DeleteClientProtocolMapper(ctx context.Context, token, realm, idOfClient, mapperID string) error + // GetKeyStoreConfig get keystoreconfig of the realm + GetKeyStoreConfig(ctx context.Context, token, realm string) (*KeyStoreConfig, error) + // GetComponents get all components in realm + GetComponents(ctx context.Context, token, realm string) ([]*Component, error) + // GetComponentsWithParams get all components in realm with query params + GetComponentsWithParams(ctx context.Context, token, realm string, params GetComponentsParams) ([]*Component, error) + // GetComponent get exactly one component by ID + GetComponent(ctx context.Context, token, realm string, componentID string) (*Component, error) + // UpdateComponent updates the given component + UpdateComponent(ctx context.Context, token, realm string, component Component) error + // GetDefaultGroups returns a list of default groups + GetDefaultGroups(ctx context.Context, token, realm string) ([]*Group, error) + // AddDefaultGroup adds group to the list of default groups + AddDefaultGroup(ctx context.Context, token, realm, groupID string) error + // RemoveDefaultGroup removes group from the list of default groups + RemoveDefaultGroup(ctx context.Context, token, realm, groupID string) error + // GetRoleMappingByGroupID gets the role mappings by group + GetRoleMappingByGroupID(ctx context.Context, token, realm, groupID string) (*MappingsRepresentation, error) + // GetRoleMappingByUserID gets the role mappings by user + GetRoleMappingByUserID(ctx context.Context, token, realm, userID string) (*MappingsRepresentation, error) + // GetGroup get group with id in realm + GetGroup(ctx context.Context, token, realm, groupID string) (*Group, error) + // GetChildGroups get child groups of group with id in realm + GetChildGroups(ctx context.Context, token, realm, groupID string, params GetChildGroupsParams) ([]*Group, error) + // GetGroupByPath get group with path in realm + GetGroupByPath(ctx context.Context, token, realm, groupPath string) (*Group, error) + // GetGroups get all groups in realm + GetGroups(ctx context.Context, token, realm string, params GetGroupsParams) ([]*Group, error) + // GetGroupManagementPermissions returns whether group Authorization permissions have been initialized or not and a reference + // to the managed permissions + GetGroupManagementPermissions(ctx context.Context, token, realm string, idOfGroup string) (*ManagementPermissionRepresentation, error) + // GetGroupsByRole gets groups assigned with a specific role of a realm + GetGroupsByRole(ctx context.Context, token, realm string, roleName string) ([]*Group, error) + // GetGroupsByClientRole gets groups with specified roles assigned of given client within a realm + GetGroupsByClientRole(ctx context.Context, token, realm string, roleName string, clientID string) ([]*Group, error) + // GetGroupsCount gets the groups count in the realm + GetGroupsCount(ctx context.Context, token, realm string, params GetGroupsParams) (int, error) + // GetGroupMembers get a list of users of group with id in realm + GetGroupMembers(ctx context.Context, token, realm, groupID string, params GetGroupsParams) ([]*User, error) + // GetClientRoles get all roles for the given client in realm + GetClientRoles(ctx context.Context, token, realm, idOfClient string, params GetRoleParams) ([]*Role, error) + // GetClientRoleByID gets role for the given client in realm using role ID + GetClientRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) + // GetClientRolesByUserID returns all client roles assigned to the given user + GetClientRolesByUserID(ctx context.Context, token, realm, idOfClient, userID string) ([]*Role, error) + // GetClientRolesByGroupID returns all client roles assigned to the given group + GetClientRolesByGroupID(ctx context.Context, token, realm, idOfClient, groupID string) ([]*Role, error) + // GetCompositeClientRolesByRoleID returns all client composite roles associated with the given client role + GetCompositeClientRolesByRoleID(ctx context.Context, token, realm, idOfClient, roleID string) ([]*Role, error) + // GetCompositeClientRolesByUserID returns all client roles and composite roles assigned to the given user + GetCompositeClientRolesByUserID(ctx context.Context, token, realm, idOfClient, userID string) ([]*Role, error) + // GetAvailableClientRolesByUserID returns all available client roles to the given user + GetAvailableClientRolesByUserID(ctx context.Context, token, realm, idOfClient, userID string) ([]*Role, error) + // GetAvailableClientRolesByGroupID returns all available roles to the given group + GetAvailableClientRolesByGroupID(ctx context.Context, token, realm, idOfClient, groupID string) ([]*Role, error) + // GetCompositeClientRolesByGroupID returns all client roles and composite roles assigned to the given group + GetCompositeClientRolesByGroupID(ctx context.Context, token, realm, idOfClient, groupID string) ([]*Role, error) + // GetClientRole get a role for the given client in a realm by role name + GetClientRole(ctx context.Context, token, realm, idOfClient, roleName string) (*Role, error) + // GetClients gets all clients in realm + GetClients(ctx context.Context, token, realm string, params GetClientsParams) ([]*Client, error) + // GetClientManagementPermissions returns whether client Authorization permissions have been initialized or not and a reference + // to the managed permissions + GetClientManagementPermissions(ctx context.Context, token, realm string, idOfClient string) (*ManagementPermissionRepresentation, error) + // CreateRealmRole creates a role in a realm + CreateRealmRole(ctx context.Context, token string, realm string, role Role) (string, error) + // GetRealmRole returns a role from a realm by role's name + GetRealmRole(ctx context.Context, token, realm, roleName string) (*Role, error) + // GetRealmRoleByID returns a role from a realm by role's ID + GetRealmRoleByID(ctx context.Context, token, realm, roleID string) (*Role, error) + // GetRealmRoles get all roles of the given realm. + GetRealmRoles(ctx context.Context, token, realm string, params GetRoleParams) ([]*Role, error) + // GetRealmRolesByUserID returns all roles assigned to the given user + GetRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) + // GetRealmRolesByGroupID returns all roles assigned to the given group + GetRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) + // UpdateRealmRole updates a role in a realm + UpdateRealmRole(ctx context.Context, token, realm, roleName string, role Role) error + // UpdateRealmRoleByID updates a role in a realm by role's ID + UpdateRealmRoleByID(ctx context.Context, token, realm, roleID string, role Role) error + // DeleteRealmRole deletes a role in a realm by role's name + DeleteRealmRole(ctx context.Context, token, realm, roleName string) error + // AddRealmRoleToUser adds realm-level role mappings + AddRealmRoleToUser(ctx context.Context, token, realm, userID string, roles []Role) error + // DeleteRealmRoleFromUser deletes realm-level role mappings + DeleteRealmRoleFromUser(ctx context.Context, token, realm, userID string, roles []Role) error + // AddRealmRoleToGroup adds realm-level role mappings + AddRealmRoleToGroup(ctx context.Context, token, realm, groupID string, roles []Role) error + // DeleteRealmRoleFromGroup deletes realm-level role mappings + DeleteRealmRoleFromGroup(ctx context.Context, token, realm, groupID string, roles []Role) error + // AddRealmRoleComposite adds a role to the composite. + AddRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error + // DeleteRealmRoleComposite deletes a role from the composite. + DeleteRealmRoleComposite(ctx context.Context, token, realm, roleName string, roles []Role) error + // GetCompositeRealmRoles returns all realm composite roles associated with the given realm role + GetCompositeRealmRoles(ctx context.Context, token, realm, roleName string) ([]*Role, error) + // GetCompositeRolesByRoleID returns all realm composite roles associated with the given client role + GetCompositeRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) + // GetCompositeRealmRolesByRoleID returns all realm composite roles associated with the given client role + GetCompositeRealmRolesByRoleID(ctx context.Context, token, realm, roleID string) ([]*Role, error) + // GetCompositeRealmRolesByUserID returns all realm roles and composite roles assigned to the given user + GetCompositeRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) + // GetCompositeRealmRolesByGroupID returns all realm roles and composite roles assigned to the given group + GetCompositeRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) + // GetAvailableRealmRolesByUserID returns all available realm roles to the given user + GetAvailableRealmRolesByUserID(ctx context.Context, token, realm, userID string) ([]*Role, error) + // GetAvailableRealmRolesByGroupID returns all available realm roles to the given group + GetAvailableRealmRolesByGroupID(ctx context.Context, token, realm, groupID string) ([]*Role, error) + // GetRealm returns top-level representation of the realm + GetRealm(ctx context.Context, token, realm string) (*RealmRepresentation, error) + // GetRealms returns top-level representation of all realms + GetRealms(ctx context.Context, token string) ([]*RealmRepresentation, error) + // CreateRealm creates a realm + CreateRealm(ctx context.Context, token string, realm RealmRepresentation) (string, error) + // UpdateRealm updates a given realm + UpdateRealm(ctx context.Context, token string, realm RealmRepresentation) error + // DeleteRealm removes a realm + DeleteRealm(ctx context.Context, token, realm string) error + // ClearRealmCache clears realm cache + ClearRealmCache(ctx context.Context, token, realm string) error + // ClearUserCache clears realm cache + ClearUserCache(ctx context.Context, token, realm string) error + // ClearKeysCache clears realm cache + ClearKeysCache(ctx context.Context, token, realm string) error + // GetAuthenticationFlows get all authentication flows from a realm + GetAuthenticationFlows(ctx context.Context, token, realm string) ([]*AuthenticationFlowRepresentation, error) + // GetAuthenticationFlow get an authentication flow with the given ID + GetAuthenticationFlow(ctx context.Context, token, realm string, authenticationFlowID string) (*AuthenticationFlowRepresentation, error) + // CreateAuthenticationFlow creates a new Authentication flow in a realm + CreateAuthenticationFlow(ctx context.Context, token, realm string, flow AuthenticationFlowRepresentation) error + // UpdateAuthenticationFlow a given Authentication Flow + UpdateAuthenticationFlow(ctx context.Context, token, realm string, flow AuthenticationFlowRepresentation, authenticationFlowID string) (*AuthenticationFlowRepresentation, error) + // DeleteAuthenticationFlow deletes a flow in a realm with the given ID + DeleteAuthenticationFlow(ctx context.Context, token, realm, flowID string) error + // GetAuthenticationExecutions retrieves all executions of a given flow + GetAuthenticationExecutions(ctx context.Context, token, realm, flow string) ([]*ModifyAuthenticationExecutionRepresentation, error) + // CreateAuthenticationExecution creates a new execution for the given flow name in the given realm + CreateAuthenticationExecution(ctx context.Context, token, realm, flow string, execution CreateAuthenticationExecutionRepresentation) error + // UpdateAuthenticationExecution updates an authentication execution for the given flow in the given realm + UpdateAuthenticationExecution(ctx context.Context, token, realm, flow string, execution ModifyAuthenticationExecutionRepresentation) error + // DeleteAuthenticationExecution delete a single execution with the given ID + DeleteAuthenticationExecution(ctx context.Context, token, realm, executionID string) error + // CreateAuthenticationExecutionFlow creates a new execution for the given flow name in the given realm + CreateAuthenticationExecutionFlow(ctx context.Context, token, realm, flow string, executionFlow CreateAuthenticationExecutionFlowRepresentation) error + // CreateUser creates the given user in the given realm and returns it's userID + // Note: Keycloak has not documented what members of the User object are actually being accepted, when creating a user. + // Things like RealmRoles must be attached using followup calls to the respective functions. + CreateUser(ctx context.Context, token, realm string, user User) (string, error) + // DeleteUser delete a given user + DeleteUser(ctx context.Context, token, realm, userID string) error + // GetUserByID fetches a user from the given realm with the given userID + GetUserByID(ctx context.Context, accessToken, realm, userID string) (*User, error) + // GetUserCount gets the user count in the realm + GetUserCount(ctx context.Context, token string, realm string, params GetUsersParams) (int, error) + // GetUserGroups get all groups for user + GetUserGroups(ctx context.Context, token, realm, userID string, params GetGroupsParams) ([]*Group, error) + // GetUserProfileConfig retrieves the user profile configuration for a realm + GetUserProfileConfig(ctx context.Context, token, realm string) (*UserProfileConfig, error) + // GetUsers get all users in realm + GetUsers(ctx context.Context, token, realm string, params GetUsersParams) ([]*User, error) + // GetUsersByRoleName returns all users have a given role + GetUsersByRoleName(ctx context.Context, token, realm, roleName string, params GetUsersByRoleParams) ([]*User, error) + // GetUsersByClientRoleName returns all users have a given client role + GetUsersByClientRoleName(ctx context.Context, token, realm, idOfClient, roleName string, params GetUsersByRoleParams) ([]*User, error) + // SetPassword sets a new password for the user with the given id. Needs elevated privileges + SetPassword(ctx context.Context, token, userID, realm, password string, temporary bool) error + // UpdateUser updates a given user + UpdateUser(ctx context.Context, token, realm string, user User) error + // AddUserToGroup puts given user to given group + AddUserToGroup(ctx context.Context, token, realm, userID, groupID string) error + // DeleteUserFromGroup deletes given user from given group + DeleteUserFromGroup(ctx context.Context, token, realm, userID, groupID string) error + // GetUserSessions returns user sessions associated with the user + GetUserSessions(ctx context.Context, token, realm, userID string) ([]*UserSessionRepresentation, error) + // GetUserOfflineSessionsForClient returns offline sessions associated with the user and client + GetUserOfflineSessionsForClient(ctx context.Context, token, realm, userID, idOfClient string) ([]*UserSessionRepresentation, error) + // AddClientRolesToUser adds client-level role mappings + AddClientRolesToUser(ctx context.Context, token, realm, idOfClient, userID string, roles []Role) error + // AddClientRoleToUser adds client-level role mappings + // + // Deprecated: replaced by AddClientRolesToUser + AddClientRoleToUser(ctx context.Context, token, realm, idOfClient, userID string, roles []Role) error + // AddClientRolesToGroup adds a client role to the group + AddClientRolesToGroup(ctx context.Context, token, realm, idOfClient, groupID string, roles []Role) error + // AddClientRoleToGroup adds a client role to the group + // + // Deprecated: replaced by AddClientRolesToGroup + AddClientRoleToGroup(ctx context.Context, token, realm, idOfClient, groupID string, roles []Role) error + // DeleteClientRolesFromUser adds client-level role mappings + DeleteClientRolesFromUser(ctx context.Context, token, realm, idOfClient, userID string, roles []Role) error + // DeleteClientRoleFromUser adds client-level role mappings + // + // Deprecated: replaced by DeleteClientRolesFrom + DeleteClientRoleFromUser(ctx context.Context, token, realm, idOfClient, userID string, roles []Role) error + // DeleteClientRoleFromGroup removes a client role from from the group + DeleteClientRoleFromGroup(ctx context.Context, token, realm, idOfClient, groupID string, roles []Role) error + // AddClientRoleComposite adds roles as composite + AddClientRoleComposite(ctx context.Context, token, realm, roleID string, roles []Role) error + // DeleteClientRoleComposite deletes composites from a role + DeleteClientRoleComposite(ctx context.Context, token, realm, roleID string, roles []Role) error + // GetUserFederatedIdentities gets all user federated identities + GetUserFederatedIdentities(ctx context.Context, token, realm, userID string) ([]*FederatedIdentityRepresentation, error) + // CreateUserFederatedIdentity creates an user federated identity + CreateUserFederatedIdentity(ctx context.Context, token, realm, userID, providerID string, federatedIdentityRep FederatedIdentityRepresentation) error + // DeleteUserFederatedIdentity deletes an user federated identity + DeleteUserFederatedIdentity(ctx context.Context, token, realm, userID, providerID string) error + // GetUserBruteForceDetectionStatus fetches a user status regarding brute force protection + GetUserBruteForceDetectionStatus(ctx context.Context, accessToken, realm, userID string) (*BruteForceStatus, error) + // CreateIdentityProvider creates an identity provider in a realm + CreateIdentityProvider(ctx context.Context, token string, realm string, providerRep IdentityProviderRepresentation) (string, error) + // GetIdentityProviders returns list of identity providers in a realm + GetIdentityProviders(ctx context.Context, token, realm string) ([]*IdentityProviderRepresentation, error) + // GetIdentityProvider gets the identity provider in a realm + GetIdentityProvider(ctx context.Context, token, realm, alias string) (*IdentityProviderRepresentation, error) + // UpdateIdentityProvider updates the identity provider in a realm + UpdateIdentityProvider(ctx context.Context, token, realm, alias string, providerRep IdentityProviderRepresentation) error + // DeleteIdentityProvider deletes the identity provider in a realm + DeleteIdentityProvider(ctx context.Context, token, realm, alias string) error + // ExportIDPPublicBrokerConfig exports the broker config for a given alias + ExportIDPPublicBrokerConfig(ctx context.Context, token, realm, alias string) (*string, error) + // ImportIdentityProviderConfig parses and returns the identity provider config at a given URL + ImportIdentityProviderConfig(ctx context.Context, token, realm, fromURL, providerID string) (map[string]string, error) + // ImportIdentityProviderConfigFromFile parses and returns the identity provider config from a given file + ImportIdentityProviderConfigFromFile(ctx context.Context, token, realm, providerID, fileName string, fileBody io.Reader) (map[string]string, error) + // CreateIdentityProviderMapper creates an instance of an identity provider mapper associated with the given alias + CreateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) (string, error) + // GetIdentityProviderMapper gets the mapper by id for the given identity provider alias in a realm + GetIdentityProviderMapper(ctx context.Context, token string, realm string, alias string, mapperID string) (*IdentityProviderMapper, error) + // DeleteIdentityProviderMapper deletes an instance of an identity provider mapper associated with the given alias and mapper ID + DeleteIdentityProviderMapper(ctx context.Context, token, realm, alias, mapperID string) error + // GetIdentityProviderMappers returns list of mappers associated with an identity provider + GetIdentityProviderMappers(ctx context.Context, token, realm, alias string) ([]*IdentityProviderMapper, error) + // GetIdentityProviderMapperByID gets the mapper of an identity provider + GetIdentityProviderMapperByID(ctx context.Context, token, realm, alias, mapperID string) (*IdentityProviderMapper, error) + // UpdateIdentityProviderMapper updates mapper of an identity provider + UpdateIdentityProviderMapper(ctx context.Context, token, realm, alias string, mapper IdentityProviderMapper) error + // GetResource returns a client's resource with the given id, using access token from admin + GetResource(ctx context.Context, token, realm, idOfClient, resourceID string) (*ResourceRepresentation, error) + // GetResourceClient returns a client's resource with the given id, using access token from client + GetResourceClient(ctx context.Context, token, realm, resourceID string) (*ResourceRepresentation, error) + // GetResources returns resources associated with the client, using access token from admin + GetResources(ctx context.Context, token, realm, idOfClient string, params GetResourceParams) ([]*ResourceRepresentation, error) + // GetResourcesClient returns resources associated with the client, using access token from client + GetResourcesClient(ctx context.Context, token, realm string, params GetResourceParams) ([]*ResourceRepresentation, error) + // GetResourceServer returns resource server settings. + // The access token must have the realm view_clients role on its service + // account to be allowed to call this endpoint. + GetResourceServer(ctx context.Context, token, realm, idOfClient string) (*ResourceServerRepresentation, error) + // UpdateResource updates a resource associated with the client, using access token from admin + UpdateResource(ctx context.Context, token, realm, idOfClient string, resource ResourceRepresentation) error + // UpdateResourceClient updates a resource associated with the client, using access token from client + UpdateResourceClient(ctx context.Context, token, realm string, resource ResourceRepresentation) error + // CreateResource creates a resource associated with the client, using access token from admin + CreateResource(ctx context.Context, token, realm string, idOfClient string, resource ResourceRepresentation) (*ResourceRepresentation, error) + // CreateResourceClient creates a resource associated with the client, using access token from client + CreateResourceClient(ctx context.Context, token, realm string, resource ResourceRepresentation) (*ResourceRepresentation, error) + // DeleteResource deletes a resource associated with the client (using an admin token) + DeleteResource(ctx context.Context, token, realm, idOfClient, resourceID string) error + // DeleteResourceClient deletes a resource associated with the client (using a client token) + DeleteResourceClient(ctx context.Context, token, realm, resourceID string) error + // GetScope returns a client's scope with the given id + GetScope(ctx context.Context, token, realm, idOfClient, scopeID string) (*ScopeRepresentation, error) + // GetScopes returns scopes associated with the client + GetScopes(ctx context.Context, token, realm, idOfClient string, params GetScopeParams) ([]*ScopeRepresentation, error) + // CreateScope creates a scope associated with the client + CreateScope(ctx context.Context, token, realm, idOfClient string, scope ScopeRepresentation) (*ScopeRepresentation, error) + // GetPermissionScope gets the permission scope associated with the client + GetPermissionScope(ctx context.Context, token, realm, idOfClient string, idOfScope string) (*PolicyRepresentation, error) + // UpdatePermissionScope updates a permission scope associated with the client + UpdatePermissionScope(ctx context.Context, token, realm, idOfClient string, idOfScope string, policy PolicyRepresentation) error + // UpdateScope updates a scope associated with the client + UpdateScope(ctx context.Context, token, realm, idOfClient string, scope ScopeRepresentation) error + // DeleteScope deletes a scope associated with the client + DeleteScope(ctx context.Context, token, realm, idOfClient, scopeID string) error + // GetPolicy returns a client's policy with the given id + GetPolicy(ctx context.Context, token, realm, idOfClient, policyID string) (*PolicyRepresentation, error) + // GetPolicies returns policies associated with the client + GetPolicies(ctx context.Context, token, realm, idOfClient string, params GetPolicyParams) ([]*PolicyRepresentation, error) + // CreatePolicy creates a policy associated with the client + CreatePolicy(ctx context.Context, token, realm, idOfClient string, policy PolicyRepresentation) (*PolicyRepresentation, error) + // UpdatePolicy updates a policy associated with the client + UpdatePolicy(ctx context.Context, token, realm, idOfClient string, policy PolicyRepresentation) error + // DeletePolicy deletes a policy associated with the client + DeletePolicy(ctx context.Context, token, realm, idOfClient, policyID string) error + // GetAuthorizationPolicyAssociatedPolicies returns a client's associated policies of specific policy with the given policy id, using access token from admin + GetAuthorizationPolicyAssociatedPolicies(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyRepresentation, error) + // GetAuthorizationPolicyResources returns a client's resources of specific policy with the given policy id, using access token from admin + GetAuthorizationPolicyResources(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyResourceRepresentation, error) + // GetAuthorizationPolicyScopes returns a client's scopes of specific policy with the given policy id, using access token from admin + GetAuthorizationPolicyScopes(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PolicyScopeRepresentation, error) + // GetResourcePolicy updates a permission for a specific resource, using token obtained by Resource Owner Password Credentials Grant or Token exchange + GetResourcePolicy(ctx context.Context, token, realm, permissionID string) (*ResourcePolicyRepresentation, error) + // GetResourcePolicies returns resources associated with the client, using token obtained by Resource Owner Password Credentials Grant or Token exchange + GetResourcePolicies(ctx context.Context, token, realm string, params GetResourcePoliciesParams) ([]*ResourcePolicyRepresentation, error) + // CreateResourcePolicy associates a permission with a specific resource, using token obtained by Resource Owner Password Credentials Grant or Token exchange + CreateResourcePolicy(ctx context.Context, token, realm, resourceID string, policy ResourcePolicyRepresentation) (*ResourcePolicyRepresentation, error) + // UpdateResourcePolicy updates a permission for a specific resource, using token obtained by Resource Owner Password Credentials Grant or Token exchange + UpdateResourcePolicy(ctx context.Context, token, realm, permissionID string, policy ResourcePolicyRepresentation) error + // DeleteResourcePolicy deletes a permission for a specific resource, using token obtained by Resource Owner Password Credentials Grant or Token exchange + DeleteResourcePolicy(ctx context.Context, token, realm, permissionID string) error + // GetPermission returns a client's permission with the given id + GetPermission(ctx context.Context, token, realm, idOfClient, permissionID string) (*PermissionRepresentation, error) + // GetDependentPermissions returns a client's permission with the given policy id + GetDependentPermissions(ctx context.Context, token, realm, idOfClient, policyID string) ([]*PermissionRepresentation, error) + // GetPermissionResources returns a client's resource attached for the given permission id + GetPermissionResources(ctx context.Context, token, realm, idOfClient, permissionID string) ([]*PermissionResource, error) + // GetScopePermissions returns permissions associated with the client scope + GetScopePermissions(ctx context.Context, token, realm, idOfClient, idOfScope string) ([]*PolicyRepresentation, error) + // GetPermissionScopes returns a client's scopes configured for the given permission id + GetPermissionScopes(ctx context.Context, token, realm, idOfClient, permissionID string) ([]*PermissionScope, error) + // GetPermissions returns permissions associated with the client + GetPermissions(ctx context.Context, token, realm, idOfClient string, params GetPermissionParams) ([]*PermissionRepresentation, error) + // CreatePermissionTicket creates a permission ticket, using access token from client + CreatePermissionTicket(ctx context.Context, token, realm string, permissions []CreatePermissionTicketParams) (*PermissionTicketResponseRepresentation, error) + // GrantUserPermission lets resource owner grant permission for specific resource ID to specific user ID + GrantUserPermission(ctx context.Context, token, realm string, permission PermissionGrantParams) (*PermissionGrantResponseRepresentation, error) + // UpdateUserProfileConfig updates the user profile configuration for a realm + UpdateUserProfileConfig(ctx context.Context, token, realm string, config UserProfileConfig) error + // UpdateUserPermission updates user permissions. + UpdateUserPermission(ctx context.Context, token, realm string, permission PermissionGrantParams) (*PermissionGrantResponseRepresentation, error) + // GetUserPermissions gets granted permissions according query parameters + GetUserPermissions(ctx context.Context, token, realm string, params GetUserPermissionParams) ([]*PermissionGrantResponseRepresentation, error) + // DeleteUserPermission revokes permissions according query parameters + DeleteUserPermission(ctx context.Context, token, realm, ticketID string) error + // CreatePermission creates a permission associated with the client + CreatePermission(ctx context.Context, token, realm, idOfClient string, permission PermissionRepresentation) (*PermissionRepresentation, error) + // UpdatePermission updates a permission associated with the client + UpdatePermission(ctx context.Context, token, realm, idOfClient string, permission PermissionRepresentation) error + // DeletePermission deletes a policy associated with the client + DeletePermission(ctx context.Context, token, realm, idOfClient, permissionID string) error + // GetCredentialRegistrators returns credentials registrators + GetCredentialRegistrators(ctx context.Context, token, realm string) ([]string, error) + // GetConfiguredUserStorageCredentialTypes returns credential types, which are provided by the user storage where user is stored + GetConfiguredUserStorageCredentialTypes(ctx context.Context, token, realm, userID string) ([]string, error) + // GetCredentials returns credentials available for a given user + GetCredentials(ctx context.Context, token, realm, userID string) ([]*CredentialRepresentation, error) + // DeleteCredentials deletes the given credential for a given user + DeleteCredentials(ctx context.Context, token, realm, userID, credentialID string) error + // UpdateCredentialUserLabel updates label for the given credential for the given user + UpdateCredentialUserLabel(ctx context.Context, token, realm, userID, credentialID, userLabel string) error + // DisableAllCredentialsByType disables all credentials for a user of a specific type + DisableAllCredentialsByType(ctx context.Context, token, realm, userID string, types []string) error + // MoveCredentialBehind move a credential to a position behind another credential + MoveCredentialBehind(ctx context.Context, token, realm, userID, credentialID, newPreviousCredentialID string) error + // MoveCredentialToFirst move a credential to a first position in the credentials list of the user + MoveCredentialToFirst(ctx context.Context, token, realm, userID, credentialID string) error + // GetEvents returns events + GetEvents(ctx context.Context, token string, realm string, params GetEventsParams) ([]*EventRepresentation, error) + // GetClientScopesScopeMappingsRealmRolesAvailable returns realm-level roles that are available to attach to this client scope + GetClientScopesScopeMappingsRealmRolesAvailable(ctx context.Context, token, realm, clientScopeID string) ([]*Role, error) + // GetClientScopesScopeMappingsRealmRoles returns roles associated with a client-scope + GetClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, clientScopeID string) ([]*Role, error) + // DeleteClientScopesScopeMappingsRealmRoles deletes realm-level roles from the client-scope + DeleteClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, clientScopeID string, roles []Role) error + // CreateClientScopesScopeMappingsRealmRoles creates realm-level roles to the client scope + CreateClientScopesScopeMappingsRealmRoles(ctx context.Context, token, realm, clientScopeID string, roles []Role) error + // RegisterRequiredAction creates a required action for a given realm + RegisterRequiredAction(ctx context.Context, token string, realm string, requiredAction RequiredActionProviderRepresentation) error + // GetUnregisteredRequiredActions gets a list of unregistered required actions for a given realm + GetUnregisteredRequiredActions(ctx context.Context, token string, realm string) ([]*UnregisteredRequiredActionProviderRepresentation, error) + // GetRequiredActions gets a list of required actions for a given realm + GetRequiredActions(ctx context.Context, token string, realm string) ([]*RequiredActionProviderRepresentation, error) + // GetRequiredAction gets a required action for a given realm + GetRequiredAction(ctx context.Context, token string, realm string, alias string) (*RequiredActionProviderRepresentation, error) + // UpdateRequiredAction updates a required action for a given realm + UpdateRequiredAction(ctx context.Context, token string, realm string, requiredAction RequiredActionProviderRepresentation) error + // DeleteRequiredAction updates a required action for a given realm + DeleteRequiredAction(ctx context.Context, token string, realm string, alias string) error + // CreateClientScopesScopeMappingsClientRoles attaches a client role to a client scope (not client's scope) + CreateClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role) error + // GetClientScopesScopeMappingsClientRolesAvailable returns available (i.e. not attached via + // CreateClientScopesScopeMappingsClientRoles) client roles for a specific client, for a client scope + // (not client's scope). + GetClientScopesScopeMappingsClientRolesAvailable(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error) + // GetClientScopesScopeMappingsClientRoles returns attached client roles for a specific client, for a client scope + // (not client's scope). + GetClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string) ([]*Role, error) + // DeleteClientScopesScopeMappingsClientRoles removes attachment of client roles from a client scope + // (not client's scope). + DeleteClientScopesScopeMappingsClientRoles(ctx context.Context, token, realm, idOfClientScope, idOfClient string, roles []Role) error + // RevokeToken revokes the passed token. The token can either be an access or refresh token. + RevokeToken(ctx context.Context, realm, clientID, clientSecret, refreshToken string) error + // UpdateUsersManagementPermissions updates the management permissions for users + UpdateUsersManagementPermissions(ctx context.Context, accessToken, realm string, managementPermissions ManagementPermissionRepresentation) (*ManagementPermissionRepresentation, error) + // GetUsersManagementPermissions returns the management permissions for users + GetUsersManagementPermissions(ctx context.Context, accessToken, realm string) (*ManagementPermissionRepresentation, error) +} diff --git a/model_test.go b/model_test.go index d14acf60..1a5affcc 100644 --- a/model_test.go +++ b/model_test.go @@ -339,6 +339,6 @@ func TestStringerOmitEmpty(t *testing.T) { } for _, custom := range customs { - assert.Equal(t, "{}", custom.(Stringable).String()) + assert.Equal(t, "{}", custom.String()) } } diff --git a/models.go b/models.go index f39a4d8c..78a69b09 100644 --- a/models.go +++ b/models.go @@ -193,6 +193,7 @@ type IntroSpectTokenResult struct { AuthTime *int `json:"auth_time,omitempty"` Jti *string `json:"jti,omitempty"` Type *string `json:"typ,omitempty"` + Azp *string `json:"azp,omitempty"` } // User represents the Keycloak User Structure @@ -351,6 +352,15 @@ type GetGroupsParams struct { Search *string `json:"search,omitempty"` } +// GetChildGroupsParams represents the optional parameters for getting child groups +type GetChildGroupsParams struct { + BriefRepresentation *bool `json:"briefRepresentation,string,omitempty"` + Exact *bool `json:"exact,string,omitempty"` + First *int `json:"first,string,omitempty"` + Max *int `json:"max,string,omitempty"` + Search *string `json:"search,omitempty"` +} + // MarshalJSON is a custom json marshaling function to automatically set the Full and BriefRepresentation properties // for backward compatibility func (obj GetGroupsParams) MarshalJSON() ([]byte, error) { @@ -408,6 +418,7 @@ type MappingsRepresentation struct { type ClientScope struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` Description *string `json:"description,omitempty"` Protocol *string `json:"protocol,omitempty"` ClientScopeAttributes *ClientScopeAttributes `json:"attributes,omitempty"` @@ -444,6 +455,7 @@ type ProtocolMappersConfig struct { AggregateAttrs *string `json:"aggregate.attrs,omitempty"` UsermodelClientRoleMappingClientID *string `json:"usermodel.clientRoleMapping.clientId,omitempty"` IncludedClientAudience *string `json:"included.client.audience,omitempty"` + IncludedCustomAudience *string `json:"included.custom.audience,omitempty"` FullPath *string `json:"full.path,omitempty"` AttributeName *string `json:"attribute.name,omitempty"` AttributeNameFormat *string `json:"attribute.nameformat,omitempty"` @@ -463,6 +475,7 @@ type Client struct { BearerOnly *bool `json:"bearerOnly,omitempty"` ClientAuthenticatorType *string `json:"clientAuthenticatorType,omitempty"` ClientID *string `json:"clientId,omitempty"` + ClientTemplate *string `json:"clientTemplate,omitempty"` ConsentRequired *bool `json:"consentRequired,omitempty"` DefaultClientScopes *[]string `json:"defaultClientScopes,omitempty"` DefaultRoles *[]string `json:"defaultRoles,omitempty"` @@ -489,6 +502,9 @@ type Client struct { ServiceAccountsEnabled *bool `json:"serviceAccountsEnabled,omitempty"` StandardFlowEnabled *bool `json:"standardFlowEnabled,omitempty"` SurrogateAuthRequired *bool `json:"surrogateAuthRequired,omitempty"` + UseTemplateConfig *bool `json:"useTemplateConfig,omitempty"` + UseTemplateMappers *bool `json:"useTemplateMappers,omitempty"` + UseTemplateScope *bool `json:"useTemplateScope,omitempty"` WebOrigins *[]string `json:"webOrigins,omitempty"` } @@ -1458,6 +1474,11 @@ type RequiredActionProviderRepresentation struct { ProviderID *string `json:"providerId,omitempty"` } +type UnregisteredRequiredActionProviderRepresentation struct { + Name *string `json:"name,omitempty"` + ProviderID *string `json:"providerId,omitempty"` +} + // ManagementPermissionRepresentation is a representation of management permissions // v18: https://www.keycloak.org/docs-api/18.0/rest-api/#_managementpermissionreference type ManagementPermissionRepresentation struct { diff --git a/pkg/jwx/jwx.go b/pkg/jwx/jwx.go index 8eeefada..8bbae255 100644 --- a/pkg/jwx/jwx.go +++ b/pkg/jwx/jwx.go @@ -109,7 +109,7 @@ func decodeRSAPublicKey(e, n *string) (*rsa.PublicKey, error) { return nil, errors.Wrap(err, errMessage) } - pKey := rsa.PublicKey{N: nInt, E: int(eInt)} + pKey := rsa.PublicKey{N: nInt, E: int(eInt)} //nolint return &pKey, nil } diff --git a/run-tests.sh b/run-tests.sh index b34cd597..db7e4bcf 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -3,8 +3,8 @@ docker compose down docker compose up -d -keycloakServer=http://localhost:8080 -url="${keycloakServer}/health" +keycloakServer=http://localhost +url="${keycloakServer}:9000/health" echo "Checking service availability at $url (CTRL+C to exit)" while true; do response=$(curl -s -o /dev/null -w "%{http_code}" $url) @@ -13,8 +13,8 @@ while true; do fi sleep 1 done -echo "Service is now available at ${keycloakServer}" +echo "Service is now available at ${keycloakServer}:8080" -go test +go test -failfast -race -cover -coverprofile=coverage.out -covermode=atomic -p 10 -cpu 1,2 -bench . -benchmem ${ARGS[@]} docker compose down