keycloak-admin 1.2.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +4 -2
- data/.github/workflows/release.yml +112 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +19 -20
- data/README.md +25 -5
- data/Rakefile +6 -0
- data/keycloak-admin.gemspec +5 -3
- data/lib/keycloak-admin/client/attack_detection_client.rb +3 -3
- data/lib/keycloak-admin/client/client.rb +18 -7
- data/lib/keycloak-admin/client/client_authz_permission_client.rb +7 -7
- data/lib/keycloak-admin/client/client_authz_policy_client.rb +5 -5
- data/lib/keycloak-admin/client/client_authz_resource_client.rb +6 -6
- data/lib/keycloak-admin/client/client_authz_scope_client.rb +5 -5
- data/lib/keycloak-admin/client/client_client.rb +6 -6
- data/lib/keycloak-admin/client/client_role_client.rb +1 -1
- data/lib/keycloak-admin/client/client_role_mappings_client.rb +2 -2
- data/lib/keycloak-admin/client/client_scope_client.rb +5 -5
- data/lib/keycloak-admin/client/client_scope_protocol_mapper_client.rb +5 -5
- data/lib/keycloak-admin/client/configurable_token_client.rb +4 -3
- data/lib/keycloak-admin/client/group_client.rb +15 -14
- data/lib/keycloak-admin/client/identity_provider_client.rb +4 -4
- data/lib/keycloak-admin/client/organization_client.rb +18 -18
- data/lib/keycloak-admin/client/realm_client.rb +4 -4
- data/lib/keycloak-admin/client/resource.rb +98 -0
- data/lib/keycloak-admin/client/response.rb +36 -0
- data/lib/keycloak-admin/client/role_client.rb +6 -6
- data/lib/keycloak-admin/client/role_mapper_client.rb +9 -8
- data/lib/keycloak-admin/client/token_client.rb +1 -3
- data/lib/keycloak-admin/client/user_client.rb +38 -31
- data/lib/keycloak-admin/configuration.rb +23 -1
- data/lib/keycloak-admin/version.rb +1 -1
- data/lib/keycloak-admin.rb +3 -1
- data/spec/client/attack_detection_client_spec.rb +3 -3
- data/spec/client/client_authz_permission_client_spec.rb +5 -5
- data/spec/client/client_authz_policy_client_spec.rb +5 -5
- data/spec/client/client_authz_resource_client_spec.rb +7 -7
- data/spec/client/client_authz_scope_client_spec.rb +5 -5
- data/spec/client/client_client_spec.rb +14 -14
- data/spec/client/client_role_mappings_client_spec.rb +11 -10
- data/spec/client/client_scope_client_spec.rb +7 -7
- data/spec/client/client_scope_protocol_mapper_client_spec.rb +7 -7
- data/spec/client/client_spec.rb +80 -7
- data/spec/client/group_client_spec.rb +47 -47
- data/spec/client/identity_provider_client_spec.rb +5 -5
- data/spec/client/organization_client_spec.rb +31 -31
- data/spec/client/realm_client_spec.rb +20 -20
- data/spec/client/resource_spec.rb +223 -0
- data/spec/client/response_spec.rb +68 -0
- data/spec/client/role_client_spec.rb +10 -10
- data/spec/client/role_mapper_client_spec.rb +10 -10
- data/spec/client/token_client_spec.rb +5 -5
- data/spec/client/user_client_spec.rb +29 -29
- data/spec/configuration_spec.rb +39 -2
- data/spec/gemspec_spec.rb +25 -0
- data/spec/integration/client_authorization_spec.rb +4 -4
- data/spec/representation/client_authz_policy_representation_spec.rb +1 -1
- data/spec/spec_helper.rb +8 -9
- metadata +42 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a9c09a6765355416960bbc5c4564ad5dd5f526f65093b43d46ac10fc39d0ae7
|
|
4
|
+
data.tar.gz: b2dd1762448fad5a096b94adc3d18b30640235fe02efa1e77269797bfa3f9464
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3274b863ef8b7d295b82d5dd87a6d3340ac96ac08c1f38c822ad531b204f4fd5755704a905ccc308821e199d17cc8c73959c3dc1d39a5a13079ce133f1ed4854
|
|
7
|
+
data.tar.gz: 7c8d7b5f54eb523f27eaaa83893d8b5190d4647ed6d7424a6ac1b55168ffa02fb426171b424330db86d976e00c84e1644d3bab3cf4fd398d1eb9cf4deff2e679
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -18,11 +18,13 @@ permissions:
|
|
|
18
18
|
|
|
19
19
|
jobs:
|
|
20
20
|
test:
|
|
21
|
-
|
|
21
|
+
name: Ruby ${{ matrix.ruby-version }}
|
|
22
|
+
runs-on: ubuntu-24.04
|
|
22
23
|
|
|
23
24
|
strategy:
|
|
25
|
+
fail-fast: false
|
|
24
26
|
matrix:
|
|
25
|
-
ruby-version: ['3.2']
|
|
27
|
+
ruby-version: ['3.1', '3.2', '3.3', '3.4']
|
|
26
28
|
|
|
27
29
|
steps:
|
|
28
30
|
- name: Start Keycloak
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishing is triggered by pushing a version tag, e.g.:
|
|
4
|
+
# git tag -a v1.2.0 -m "Version 1.2.0" && git push origin v1.2.0
|
|
5
|
+
#
|
|
6
|
+
# No RubyGems API key is stored in this repository: the gem is pushed through
|
|
7
|
+
# RubyGems' Trusted Publishing, which exchanges a short-lived GitHub OIDC token
|
|
8
|
+
# for a scoped RubyGems credential. This requires a trusted publisher declared on
|
|
9
|
+
# https://rubygems.org/gems/keycloak-admin pointing to this repository, this
|
|
10
|
+
# workflow file name and the 'release' environment.
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags: [ "v*" ]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release:
|
|
20
|
+
name: Push gem to RubyGems.org
|
|
21
|
+
# Pinned rather than 'ubuntu-latest', for the same reason as in ci.yml.
|
|
22
|
+
runs-on: ubuntu-24.04
|
|
23
|
+
|
|
24
|
+
environment: release
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: write # 'rake release' pushes the version tag when it does not exist yet
|
|
28
|
+
id-token: write # mandatory to request the OIDC token used by trusted publishing
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
# The test suite includes an integration spec that talks to a real Keycloak
|
|
32
|
+
# (spec/integration/client_authorization_spec.rb, gated on ENV["GITHUB_ACTIONS"],
|
|
33
|
+
# which GitHub Actions sets on every run regardless of workflow). Keycloak is
|
|
34
|
+
# provisioned here the same way ci.yml does it, so 'bundle exec rspec' below is a
|
|
35
|
+
# real, complete gate rather than skipping that spec for lack of a server.
|
|
36
|
+
- name: Start Keycloak
|
|
37
|
+
run: |
|
|
38
|
+
docker run -d --name keycloak -p 8080:8080 \
|
|
39
|
+
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
40
|
+
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
|
|
41
|
+
-e KC_HOSTNAME=http://localhost:8080 \
|
|
42
|
+
-e KC_HOSTNAME_ADMIN=http://localhost:8080 \
|
|
43
|
+
-e KC_HTTP_ENABLED=true \
|
|
44
|
+
-e KC_DB=dev-file \
|
|
45
|
+
quay.io/keycloak/keycloak:26.7.0 \
|
|
46
|
+
start-dev
|
|
47
|
+
|
|
48
|
+
- name: Wait for Keycloak to be ready
|
|
49
|
+
run: |
|
|
50
|
+
for i in $(seq 1 30); do
|
|
51
|
+
if curl --silent --fail http://localhost:8080/realms/master > /dev/null; then
|
|
52
|
+
echo "Keycloak is ready"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
sleep 5
|
|
56
|
+
done
|
|
57
|
+
echo "Keycloak did not become ready in time"
|
|
58
|
+
docker logs keycloak
|
|
59
|
+
exit 1
|
|
60
|
+
|
|
61
|
+
- name: create realm
|
|
62
|
+
run: |
|
|
63
|
+
TOKEN=$(curl --silent --location --request POST "http://localhost:8080/realms/master/protocol/openid-connect/token" \
|
|
64
|
+
--header 'Content-Type: application/x-www-form-urlencoded' \
|
|
65
|
+
--data-urlencode 'grant_type=password' \
|
|
66
|
+
--data-urlencode 'username=admin' \
|
|
67
|
+
--data-urlencode 'password=admin' \
|
|
68
|
+
--data-urlencode 'client_id=admin-cli' | jq -r '.access_token')
|
|
69
|
+
|
|
70
|
+
curl --silent --show-error -L -X POST "http://localhost:8080/admin/realms" \
|
|
71
|
+
--header "Content-Type: application/json" \
|
|
72
|
+
--header "Authorization: Bearer ${TOKEN}" \
|
|
73
|
+
--data '{"realm":"dummy","enabled":true}'
|
|
74
|
+
|
|
75
|
+
curl --silent --show-error --request POST 'http://localhost:8080/admin/realms/dummy/clients' \
|
|
76
|
+
--header 'Authorization: Bearer '$TOKEN \
|
|
77
|
+
--header 'Content-Type: application/json' \
|
|
78
|
+
--data-raw '{
|
|
79
|
+
"clientId":"dummy-client",
|
|
80
|
+
"enabled":true,
|
|
81
|
+
"consentRequired": false,
|
|
82
|
+
"attributes":{},
|
|
83
|
+
"serviceAccountsEnabled": true,
|
|
84
|
+
"protocol":"openid-connect",
|
|
85
|
+
"publicClient":false,
|
|
86
|
+
"authorizationServicesEnabled": true,
|
|
87
|
+
"clientAuthenticatorType":"client-secret",
|
|
88
|
+
"redirectUris":["http://localhost:8180/demo"]
|
|
89
|
+
}'
|
|
90
|
+
|
|
91
|
+
- uses: actions/checkout@v4
|
|
92
|
+
|
|
93
|
+
- name: Set up Ruby
|
|
94
|
+
uses: ruby/setup-ruby@v1
|
|
95
|
+
with:
|
|
96
|
+
ruby-version: '3.4'
|
|
97
|
+
bundler-cache: true
|
|
98
|
+
|
|
99
|
+
- name: Check that the tag matches KeycloakAdmin::VERSION
|
|
100
|
+
run: |
|
|
101
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
102
|
+
version="$(ruby -Ilib -r keycloak-admin/version -e 'print KeycloakAdmin::VERSION')"
|
|
103
|
+
if [ "$tag" != "$version" ]; then
|
|
104
|
+
echo "::error::Tag '${GITHUB_REF_NAME}' does not match KeycloakAdmin::VERSION '${version}'"
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
- name: Run tests
|
|
109
|
+
run: bundle exec rspec
|
|
110
|
+
|
|
111
|
+
# Builds the gem, pushes it to RubyGems.org and waits for it to be available.
|
|
112
|
+
- uses: rubygems/release-gem@v1
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.1] - 2026-07-31
|
|
9
|
+
|
|
10
|
+
* [Feature] Requests are now logged through `config.logger` (method, URL, and response status) via Faraday's `:logger` middleware.
|
|
11
|
+
* [Feature] The access token is now cached and reused (tracking `expires_in`) instead of being fetched before nearly every call. Previously, caching lived on the `Client` instance, but a fresh `Client` subclass is created for almost every call (e.g. `KeycloakAdmin.realm(x).users` builds a new `UserClient`), so the cache was rarely hit in practice; it now lives on the shared `Configuration`.
|
|
12
|
+
* Publish the gem to RubyGems from Github Actions when a `v*` tag is pushed, using RubyGems' Trusted Publishing
|
|
13
|
+
|
|
14
|
+
## [2.0.0] - 2026-07-31
|
|
15
|
+
|
|
16
|
+
* [Breaking] Replaced `rest-client` with `Faraday` as the underlying HTTP library. This should be transparent for callers of this gem's own API, but `config.rest_client_options` is renamed to `config.faraday_options` and its shape changes from rest-client's flat hash to Faraday's connection options (e.g. `{ timeout: 5 }` becomes `{ request: { timeout: 5 } }`, `{ verify_ssl: false }` becomes `{ ssl: { verify: false } }`). See the `Configuration` section of the README.
|
|
17
|
+
* [Fix] `GroupClient#remove_realm_level_role_name!` used Ruby 3.1 hash value omission
|
|
18
|
+
* [Fix] Strip trailing slashes from the configured `server_url`. Keycloak 26 rejects non-normalized request paths with `400 {"error":"missingNormalization"}`, so a `server_url` such as `http://localhost:8080/` produced an unusable `//realms/...` path. Earlier Keycloak versions tolerated it.
|
|
19
|
+
* [Chore] `required_ruby_version` is now `>= 3.1` instead of `>= 2.3`
|
|
20
|
+
* [Chore] CI now tests against Ruby 3.1, 3.2, 3.3 and 3.4.
|
|
21
|
+
|
|
8
22
|
## [1.2.0] - 2026-07-30
|
|
9
23
|
|
|
10
24
|
* [Chore] Bump `http-cookie` dependency from 1.1.0 to 1.1.6.
|
data/Gemfile.lock
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
keycloak-admin (
|
|
4
|
+
keycloak-admin (2.0.1)
|
|
5
|
+
base64
|
|
6
|
+
faraday (~> 2.0)
|
|
5
7
|
http-cookie (~> 1.0, >= 1.0.3)
|
|
6
|
-
rest-client (~> 2.0)
|
|
7
8
|
|
|
8
9
|
GEM
|
|
9
10
|
remote: https://rubygems.org/
|
|
10
11
|
specs:
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
base64 (0.3.0)
|
|
13
|
+
byebug (12.0.0)
|
|
13
14
|
diff-lcs (1.6.2)
|
|
14
15
|
domain_name (0.6.20240107)
|
|
15
|
-
|
|
16
|
+
faraday (2.14.3)
|
|
17
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
18
|
+
json
|
|
19
|
+
logger
|
|
20
|
+
faraday-net_http (3.4.4)
|
|
21
|
+
net-http (~> 0.5)
|
|
16
22
|
http-cookie (1.1.6)
|
|
17
23
|
domain_name (~> 0.5)
|
|
18
|
-
|
|
24
|
+
json (2.21.2)
|
|
19
25
|
logger (1.7.0)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
mime-types-data (3.2026.0701)
|
|
24
|
-
netrc (0.11.0)
|
|
25
|
-
reline (0.6.3)
|
|
26
|
-
io-console (~> 0.5)
|
|
27
|
-
rest-client (2.1.0)
|
|
28
|
-
http-accept (>= 1.7.0, < 2.0)
|
|
29
|
-
http-cookie (>= 1.0.2, < 2.0)
|
|
30
|
-
mime-types (>= 1.16, < 4.0)
|
|
31
|
-
netrc (~> 0.8)
|
|
26
|
+
net-http (0.9.1)
|
|
27
|
+
uri (>= 0.11.1)
|
|
28
|
+
rake (13.4.2)
|
|
32
29
|
rspec (3.13.2)
|
|
33
30
|
rspec-core (~> 3.13.0)
|
|
34
31
|
rspec-expectations (~> 3.13.0)
|
|
@@ -42,14 +39,16 @@ GEM
|
|
|
42
39
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
43
40
|
rspec-support (~> 3.13.0)
|
|
44
41
|
rspec-support (3.13.7)
|
|
42
|
+
uri (1.1.1)
|
|
45
43
|
|
|
46
44
|
PLATFORMS
|
|
47
45
|
ruby
|
|
48
46
|
|
|
49
47
|
DEPENDENCIES
|
|
50
|
-
byebug (=
|
|
48
|
+
byebug (= 12.0.0)
|
|
51
49
|
keycloak-admin!
|
|
50
|
+
rake (>= 13.0)
|
|
52
51
|
rspec (= 3.13.2)
|
|
53
52
|
|
|
54
53
|
BUNDLED WITH
|
|
55
|
-
2.
|
|
54
|
+
2.6.9
|
data/README.md
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
# Keycloak Admin Ruby
|
|
3
3
|
|
|
4
4
|
Ruby client that acts as a client for the Keycloak REST API.
|
|
5
|
-
This gem basically acts as an url builder using `
|
|
5
|
+
This gem basically acts as an url builder using `Faraday` to get responses and serialize them into _representation_ objects.
|
|
6
6
|
|
|
7
7
|
_Warning: This beta gem is currently used for personal use. Most Keycloak Admin features are not implemented yet._
|
|
8
8
|
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
Ruby 3.1 or greater. The gem is tested against Ruby 3.1, 3.2, 3.3 and 3.4.
|
|
12
|
+
|
|
9
13
|
## Install
|
|
10
14
|
|
|
11
15
|
This gem *does not* require Rails.
|
|
12
16
|
For example, using `bundle`, add this line to your Gemfile.
|
|
13
17
|
|
|
14
18
|
```ruby
|
|
15
|
-
gem "keycloak-admin", "
|
|
19
|
+
gem "keycloak-admin", "2.0.1"
|
|
16
20
|
```
|
|
17
21
|
|
|
18
22
|
## Login
|
|
@@ -81,8 +85,8 @@ KeycloakAdmin.configure do |config|
|
|
|
81
85
|
config.password = ENV["KEYCLOAK_ADMIN_PASSWORD"]
|
|
82
86
|
config.logger = Rails.logger
|
|
83
87
|
|
|
84
|
-
# You configure
|
|
85
|
-
config.
|
|
88
|
+
# You configure Faraday to your liking – see https://lostisland.github.io/faraday/#/customization/connection-options for available options.
|
|
89
|
+
config.faraday_options = { request: { timeout: 5 } }
|
|
86
90
|
end
|
|
87
91
|
```
|
|
88
92
|
This example is autoloaded in a Rails environment.
|
|
@@ -102,7 +106,7 @@ All options have a default value. However, all of them can be changed in your in
|
|
|
102
106
|
| `username` | `nil`| String | Optional | Username to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `mummy` |
|
|
103
107
|
| `password` | `nil`| String | Optional | Clear password to access the Admin REST API. Recommended if `user_service_account` is set to `false`. | `bobby` |
|
|
104
108
|
| `logger` | `Logger.new(STDOUT)`| Logger | Optional | The logger used by `keycloak-admin` | `Rails.logger` |
|
|
105
|
-
| `
|
|
109
|
+
| `faraday_options` | `{}`| Hash | Optional | Options to pass to `Faraday.new` (e.g. `request:`, `ssl:`, `proxy:`) | `{ request: { timeout: 5 } }` |
|
|
106
110
|
|
|
107
111
|
|
|
108
112
|
## Use Cases
|
|
@@ -881,3 +885,19 @@ From the `keycloak-admin-api` directory:
|
|
|
881
885
|
$ docker build . -t keycloak-admin:test
|
|
882
886
|
$ docker run -v `pwd`:/usr/src/app/ keycloak-admin:test rspec spec
|
|
883
887
|
```
|
|
888
|
+
|
|
889
|
+
## How to release a new version
|
|
890
|
+
|
|
891
|
+
Releases are published to [RubyGems](https://rubygems.org/gems/keycloak-admin) by GitHub Actions
|
|
892
|
+
(`.github/workflows/release.yml`), through [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/):
|
|
893
|
+
no API key is stored in this repository, the workflow exchanges a short-lived GitHub OIDC token for a
|
|
894
|
+
scoped RubyGems credential.
|
|
895
|
+
|
|
896
|
+
1. Update `KeycloakAdmin::VERSION` in `lib/keycloak-admin/version.rb` and the `CHANGELOG.md`
|
|
897
|
+
2. Commit and push these changes to `main`
|
|
898
|
+
3. Tag the commit and push the tag:
|
|
899
|
+
|
|
900
|
+
```
|
|
901
|
+
$ git tag -a v2.0.1 -m "Version 2.0.1"
|
|
902
|
+
$ git push origin v2.0.1
|
|
903
|
+
```
|
data/Rakefile
ADDED
data/keycloak-admin.gemspec
CHANGED
|
@@ -15,10 +15,12 @@ Gem::Specification.new do |spec|
|
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
|
16
16
|
spec.require_paths = ["lib"]
|
|
17
17
|
|
|
18
|
-
spec.required_ruby_version = '>=
|
|
18
|
+
spec.required_ruby_version = '>= 3.1'
|
|
19
19
|
|
|
20
20
|
spec.add_dependency "http-cookie", "~> 1.0", ">= 1.0.3"
|
|
21
|
-
spec.add_dependency "
|
|
21
|
+
spec.add_dependency "faraday", "~> 2.0"
|
|
22
|
+
spec.add_dependency "base64"
|
|
22
23
|
spec.add_development_dependency "rspec", "3.13.2"
|
|
23
|
-
spec.add_development_dependency "byebug", "
|
|
24
|
+
spec.add_development_dependency "byebug", "12.0.0"
|
|
25
|
+
spec.add_development_dependency "rake", ">= 13.0"
|
|
24
26
|
end
|
|
@@ -10,7 +10,7 @@ module KeycloakAdmin
|
|
|
10
10
|
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
11
11
|
|
|
12
12
|
response = execute_http do
|
|
13
|
-
|
|
13
|
+
resource(brute_force_url(user_id)).get(headers)
|
|
14
14
|
end
|
|
15
15
|
AttackDetectionRepresentation.from_hash(JSON.parse(response))
|
|
16
16
|
end
|
|
@@ -19,14 +19,14 @@ module KeycloakAdmin
|
|
|
19
19
|
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
20
20
|
|
|
21
21
|
execute_http do
|
|
22
|
-
|
|
22
|
+
resource(brute_force_url(user_id)).delete(headers)
|
|
23
23
|
end
|
|
24
24
|
true
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def unlock_users
|
|
28
28
|
execute_http do
|
|
29
|
-
|
|
29
|
+
resource(brute_force_url).delete(headers)
|
|
30
30
|
end
|
|
31
31
|
true
|
|
32
32
|
end
|
|
@@ -6,11 +6,14 @@ module KeycloakAdmin
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def server_url
|
|
9
|
-
@configuration.server_url
|
|
9
|
+
@configuration.server_url&.sub(/\/+\z/, "")
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Cached on @configuration, not on this instance, since a new Client subclass is
|
|
13
|
+
# created for nearly every call (e.g. KeycloakAdmin.realm(x).users creates a fresh
|
|
14
|
+
# UserClient) - caching here alone would fetch a new token on almost every request.
|
|
12
15
|
def current_token
|
|
13
|
-
@
|
|
16
|
+
@configuration.cached_token || @configuration.cache_token(fetch_token)
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def headers
|
|
@@ -23,15 +26,15 @@ module KeycloakAdmin
|
|
|
23
26
|
|
|
24
27
|
def execute_http
|
|
25
28
|
yield
|
|
26
|
-
rescue
|
|
29
|
+
rescue Faraday::TimeoutError
|
|
27
30
|
raise
|
|
28
|
-
rescue
|
|
31
|
+
rescue Faraday::ClientError, Faraday::ServerError => e
|
|
29
32
|
http_error(e.response)
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
def created_id(response)
|
|
33
|
-
unless response.
|
|
34
|
-
raise "Create method returned status #{response.
|
|
36
|
+
unless response.status == 201
|
|
37
|
+
raise "Create method returned status #{response.reason_phrase} (Code: #{response.status}); expected status: Created (201)"
|
|
35
38
|
end
|
|
36
39
|
(_head, _separator, id) = response.headers[:location].rpartition("/")
|
|
37
40
|
id
|
|
@@ -49,8 +52,16 @@ module KeycloakAdmin
|
|
|
49
52
|
|
|
50
53
|
private
|
|
51
54
|
|
|
55
|
+
def fetch_token
|
|
56
|
+
KeycloakAdmin.create_client(@configuration, @configuration.client_realm_name).token.get
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def resource(url)
|
|
60
|
+
Resource.new(url, @configuration.faraday_options, @configuration.logger)
|
|
61
|
+
end
|
|
62
|
+
|
|
52
63
|
def http_error(response)
|
|
53
|
-
raise "Keycloak: The request failed with response code #{response
|
|
64
|
+
raise "Keycloak: The request failed with response code #{response[:status]} and message: #{response[:body]}"
|
|
54
65
|
end
|
|
55
66
|
end
|
|
56
67
|
end
|
|
@@ -13,15 +13,15 @@ module KeycloakAdmin
|
|
|
13
13
|
|
|
14
14
|
def delete(permission_id)
|
|
15
15
|
execute_http do
|
|
16
|
-
|
|
16
|
+
resource(authz_permission_url(@client_id, nil, @type, permission_id)).delete(headers)
|
|
17
17
|
end
|
|
18
18
|
true
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def find_by(name,
|
|
21
|
+
def find_by(name, resource_param, scope = nil)
|
|
22
22
|
response = execute_http do
|
|
23
|
-
url = "#{authz_permission_url(@client_id)}?name=#{name}&resource=#{
|
|
24
|
-
|
|
23
|
+
url = "#{authz_permission_url(@client_id)}?name=#{name}&resource=#{resource_param}&type=#{@type}&scope=#{scope}&deep=true&first=0&max=100"
|
|
24
|
+
resource(url).get(headers)
|
|
25
25
|
end
|
|
26
26
|
JSON.parse(response).map { |role_as_hash| ClientAuthzPermissionRepresentation.from_hash(role_as_hash) }
|
|
27
27
|
end
|
|
@@ -33,7 +33,7 @@ module KeycloakAdmin
|
|
|
33
33
|
|
|
34
34
|
def save(permission_representation)
|
|
35
35
|
execute_http do
|
|
36
|
-
|
|
36
|
+
resource(authz_permission_url(@client_id, nil, permission_representation.type)).post(
|
|
37
37
|
create_payload(permission_representation), headers
|
|
38
38
|
)
|
|
39
39
|
end
|
|
@@ -41,14 +41,14 @@ module KeycloakAdmin
|
|
|
41
41
|
|
|
42
42
|
def list
|
|
43
43
|
response = execute_http do
|
|
44
|
-
|
|
44
|
+
resource(authz_permission_url(@client_id, @resource_id)).get(headers)
|
|
45
45
|
end
|
|
46
46
|
JSON.parse(response).map { |role_as_hash| ClientAuthzPermissionRepresentation.from_hash(role_as_hash) }
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def get(permission_id)
|
|
50
50
|
response = execute_http do
|
|
51
|
-
|
|
51
|
+
resource(authz_permission_url(@client_id, nil, @type, permission_id)).get(headers)
|
|
52
52
|
end
|
|
53
53
|
ClientAuthzPermissionRepresentation.from_hash(JSON.parse(response))
|
|
54
54
|
end
|
|
@@ -18,7 +18,7 @@ module KeycloakAdmin
|
|
|
18
18
|
|
|
19
19
|
def save(policy_representation)
|
|
20
20
|
execute_http do
|
|
21
|
-
|
|
21
|
+
resource(authz_policy_url(@client_id, @type)).post(
|
|
22
22
|
create_payload(policy_representation), headers
|
|
23
23
|
)
|
|
24
24
|
end
|
|
@@ -26,7 +26,7 @@ module KeycloakAdmin
|
|
|
26
26
|
|
|
27
27
|
def get(policy_id)
|
|
28
28
|
response = execute_http do
|
|
29
|
-
|
|
29
|
+
resource(authz_policy_url(@client_id, @type, policy_id)).get(headers)
|
|
30
30
|
end
|
|
31
31
|
ClientAuthzPolicyRepresentation.from_hash(JSON.parse(response))
|
|
32
32
|
end
|
|
@@ -34,21 +34,21 @@ module KeycloakAdmin
|
|
|
34
34
|
def find_by(name, type)
|
|
35
35
|
response = execute_http do
|
|
36
36
|
url = "#{authz_policy_url(@client_id, @type)}?permission=false&name=#{name}&type=#{type}&first=0&max=11"
|
|
37
|
-
|
|
37
|
+
resource(url).get(headers)
|
|
38
38
|
end
|
|
39
39
|
JSON.parse(response).map { |role_as_hash| ClientAuthzPolicyRepresentation.from_hash(role_as_hash) }
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def delete(policy_id)
|
|
43
43
|
execute_http do
|
|
44
|
-
|
|
44
|
+
resource(authz_policy_url(@client_id, @type, policy_id)).delete(headers)
|
|
45
45
|
end
|
|
46
46
|
true
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def list
|
|
50
50
|
response = execute_http do
|
|
51
|
-
|
|
51
|
+
resource(authz_policy_url(@client_id, @type)).get(headers)
|
|
52
52
|
end
|
|
53
53
|
JSON.parse(response).map { |role_as_hash| ClientAuthzPolicyRepresentation.from_hash(role_as_hash) }
|
|
54
54
|
end
|
|
@@ -9,14 +9,14 @@ module KeycloakAdmin
|
|
|
9
9
|
|
|
10
10
|
def list
|
|
11
11
|
response = execute_http do
|
|
12
|
-
|
|
12
|
+
resource(authz_resources_url(@client_id)).get(headers)
|
|
13
13
|
end
|
|
14
14
|
JSON.parse(response).map { |role_as_hash| ClientAuthzResourceRepresentation.from_hash(role_as_hash) }
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def get(resource_id)
|
|
18
18
|
response = execute_http do
|
|
19
|
-
|
|
19
|
+
resource(authz_resources_url(@client_id, resource_id)).get(headers)
|
|
20
20
|
end
|
|
21
21
|
ClientAuthzResourceRepresentation.from_hash(JSON.parse(response))
|
|
22
22
|
end
|
|
@@ -36,7 +36,7 @@ module KeycloakAdmin
|
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
execute_http do
|
|
39
|
-
|
|
39
|
+
resource(authz_resources_url(@client_id, resource_id)).put(new_resource.to_json, headers)
|
|
40
40
|
end
|
|
41
41
|
get(resource_id)
|
|
42
42
|
end
|
|
@@ -48,21 +48,21 @@ module KeycloakAdmin
|
|
|
48
48
|
def find_by(name, type, uris, owner, scope)
|
|
49
49
|
response = execute_http do
|
|
50
50
|
url = "#{authz_resources_url(@client_id)}?name=#{name}&type=#{type}&uris=#{uris}&owner=#{owner}&scope=#{scope}&deep=true&first=0&max=100"
|
|
51
|
-
|
|
51
|
+
resource(url).get(headers)
|
|
52
52
|
end
|
|
53
53
|
JSON.parse(response).map { |role_as_hash| ClientAuthzResourceRepresentation.from_hash(role_as_hash) }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def save(client_authz_resource_representation)
|
|
57
57
|
response = execute_http do
|
|
58
|
-
|
|
58
|
+
resource(authz_resources_url(@client_id)).post(client_authz_resource_representation.to_json, headers)
|
|
59
59
|
end
|
|
60
60
|
ClientAuthzResourceRepresentation.from_hash(JSON.parse(response))
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def delete(resource_id)
|
|
64
64
|
execute_http do
|
|
65
|
-
|
|
65
|
+
resource(authz_resources_url(@client_id, resource_id)).delete(headers)
|
|
66
66
|
end
|
|
67
67
|
true
|
|
68
68
|
end
|
|
@@ -15,21 +15,21 @@ module KeycloakAdmin
|
|
|
15
15
|
|
|
16
16
|
def list
|
|
17
17
|
response = execute_http do
|
|
18
|
-
|
|
18
|
+
resource(authz_scopes_url(@client_id, @resource_id)).get(headers)
|
|
19
19
|
end
|
|
20
20
|
JSON.parse(response).map { |role_as_hash| ClientAuthzScopeRepresentation.from_hash(role_as_hash) }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def delete(scope_id)
|
|
24
24
|
execute_http do
|
|
25
|
-
|
|
25
|
+
resource(authz_scopes_url(@client_id, nil, scope_id)).delete(headers)
|
|
26
26
|
end
|
|
27
27
|
true
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def get(scope_id)
|
|
31
31
|
response = execute_http do
|
|
32
|
-
|
|
32
|
+
resource(authz_scopes_url(@client_id, nil, scope_id)).get(headers)
|
|
33
33
|
end
|
|
34
34
|
ClientAuthzScopeRepresentation.from_hash(JSON.parse(response))
|
|
35
35
|
end
|
|
@@ -37,7 +37,7 @@ module KeycloakAdmin
|
|
|
37
37
|
def search(name)
|
|
38
38
|
url = "#{authz_scopes_url(@client_id)}?first=0&max=11&deep=false&name=#{name}"
|
|
39
39
|
response = execute_http do
|
|
40
|
-
|
|
40
|
+
resource(url).get(headers)
|
|
41
41
|
end
|
|
42
42
|
JSON.parse(response).map { |role_as_hash| ClientAuthzScopeRepresentation.from_hash(role_as_hash) }
|
|
43
43
|
end
|
|
@@ -54,7 +54,7 @@ module KeycloakAdmin
|
|
|
54
54
|
|
|
55
55
|
def save(scope_representation)
|
|
56
56
|
execute_http do
|
|
57
|
-
|
|
57
|
+
resource(authz_scopes_url(@client_id)).post(
|
|
58
58
|
create_payload(scope_representation), headers
|
|
59
59
|
)
|
|
60
60
|
end
|
|
@@ -8,14 +8,14 @@ module KeycloakAdmin
|
|
|
8
8
|
|
|
9
9
|
def get(id)
|
|
10
10
|
response = execute_http do
|
|
11
|
-
|
|
11
|
+
resource(clients_url(id)).get(headers)
|
|
12
12
|
end
|
|
13
13
|
ClientRepresentation.from_hash(JSON.parse(response))
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def save(client_representation)
|
|
17
17
|
execute_http do
|
|
18
|
-
|
|
18
|
+
resource(clients_url).post(
|
|
19
19
|
create_payload(client_representation), headers
|
|
20
20
|
)
|
|
21
21
|
end
|
|
@@ -23,7 +23,7 @@ module KeycloakAdmin
|
|
|
23
23
|
|
|
24
24
|
def list
|
|
25
25
|
response = execute_http do
|
|
26
|
-
|
|
26
|
+
resource(clients_url).get(headers)
|
|
27
27
|
end
|
|
28
28
|
JSON.parse(response).map { |client_as_hash| ClientRepresentation.from_hash(client_as_hash) }
|
|
29
29
|
end
|
|
@@ -34,14 +34,14 @@ module KeycloakAdmin
|
|
|
34
34
|
|
|
35
35
|
def delete(id)
|
|
36
36
|
execute_http do
|
|
37
|
-
|
|
37
|
+
resource(clients_url(id)).delete(headers)
|
|
38
38
|
end
|
|
39
39
|
true
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def update(client_representation)
|
|
43
43
|
execute_http do
|
|
44
|
-
|
|
44
|
+
resource(clients_url(client_representation.id)).put(
|
|
45
45
|
create_payload(client_representation), headers
|
|
46
46
|
)
|
|
47
47
|
end
|
|
@@ -51,7 +51,7 @@ module KeycloakAdmin
|
|
|
51
51
|
|
|
52
52
|
def get_service_account_user(client_id)
|
|
53
53
|
response = execute_http do
|
|
54
|
-
|
|
54
|
+
resource(service_account_user_url(client_id)).get(headers)
|
|
55
55
|
end
|
|
56
56
|
UserRepresentation.from_hash(JSON.parse(response))
|
|
57
57
|
end
|
|
@@ -8,7 +8,7 @@ module KeycloakAdmin
|
|
|
8
8
|
|
|
9
9
|
def list(client_id)
|
|
10
10
|
response = execute_http do
|
|
11
|
-
|
|
11
|
+
resource(clients_url(client_id)).get(headers)
|
|
12
12
|
end
|
|
13
13
|
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
|
14
14
|
end
|