keycloak-admin 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -100
- data/README.md +46 -10
- data/keycloak-admin.gemspec +1 -1
- data/lib/keycloak-admin.rb +7 -6
- data/lib/keycloak-admin/client/client.rb +8 -2
- data/lib/keycloak-admin/client/token_client.rb +3 -10
- data/lib/keycloak-admin/client/user_client.rb +17 -21
- data/lib/keycloak-admin/configuration.rb +49 -2
- data/lib/keycloak-admin/representation/token_representation.rb +29 -4
- data/lib/keycloak-admin/version.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 530efac24194a86644d0924ffd5a0aa931c6928d
|
4
|
+
data.tar.gz: 9e42d8ceed9f876549dbd2cf2fd86c12ad01c500
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6586f5fbd3b14cc579f8d77ff8cb8ee963eb013da1bd5d3807c6cfabe366b6b8340356c8d8cbc0207d4bf8eefaf8964a0717e0a25145d679b23d427f46641683
|
7
|
+
data.tar.gz: 4891b3ad7ffc41969c8e9a7b3624315f57edef8f9253dea0218c195c5a3eec1b019356ca2d75e0e1cc180ccb731f3ce7e47534335ea1ed915dcee63003604767
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,98 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
keycloak-admin (0.
|
4
|
+
keycloak-admin (0.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
actioncable (5.1.4)
|
10
|
-
actionpack (= 5.1.4)
|
11
|
-
nio4r (~> 2.0)
|
12
|
-
websocket-driver (~> 0.6.1)
|
13
|
-
actionmailer (5.1.4)
|
14
|
-
actionpack (= 5.1.4)
|
15
|
-
actionview (= 5.1.4)
|
16
|
-
activejob (= 5.1.4)
|
17
|
-
mail (~> 2.5, >= 2.5.4)
|
18
|
-
rails-dom-testing (~> 2.0)
|
19
|
-
actionpack (5.1.4)
|
20
|
-
actionview (= 5.1.4)
|
21
|
-
activesupport (= 5.1.4)
|
22
|
-
rack (~> 2.0)
|
23
|
-
rack-test (>= 0.6.3)
|
24
|
-
rails-dom-testing (~> 2.0)
|
25
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
26
|
-
actionview (5.1.4)
|
27
|
-
activesupport (= 5.1.4)
|
28
|
-
builder (~> 3.1)
|
29
|
-
erubi (~> 1.4)
|
30
|
-
rails-dom-testing (~> 2.0)
|
31
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
32
|
-
activejob (5.1.4)
|
33
|
-
activesupport (= 5.1.4)
|
34
|
-
globalid (>= 0.3.6)
|
35
|
-
activemodel (5.1.4)
|
36
|
-
activesupport (= 5.1.4)
|
37
|
-
activerecord (5.1.4)
|
38
|
-
activemodel (= 5.1.4)
|
39
|
-
activesupport (= 5.1.4)
|
40
|
-
arel (~> 8.0)
|
41
|
-
activesupport (5.1.4)
|
42
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
43
|
-
i18n (~> 0.7)
|
44
|
-
minitest (~> 5.1)
|
45
|
-
tzinfo (~> 1.1)
|
46
|
-
arel (8.0.0)
|
47
|
-
builder (3.2.3)
|
48
9
|
byebug (9.1.0)
|
49
|
-
concurrent-ruby (1.0.5)
|
50
|
-
crass (1.0.3)
|
51
10
|
diff-lcs (1.3)
|
52
|
-
erubi (1.7.0)
|
53
|
-
globalid (0.4.1)
|
54
|
-
activesupport (>= 4.2.0)
|
55
|
-
i18n (0.9.1)
|
56
|
-
concurrent-ruby (~> 1.0)
|
57
|
-
loofah (2.1.1)
|
58
|
-
crass (~> 1.0.2)
|
59
|
-
nokogiri (>= 1.5.9)
|
60
|
-
mail (2.7.0)
|
61
|
-
mini_mime (>= 0.1.1)
|
62
|
-
method_source (0.9.0)
|
63
|
-
mini_mime (1.0.0)
|
64
|
-
mini_portile2 (2.3.0)
|
65
|
-
minitest (5.11.1)
|
66
|
-
nio4r (2.2.0)
|
67
|
-
nokogiri (1.8.1)
|
68
|
-
mini_portile2 (~> 2.3.0)
|
69
|
-
rack (2.0.3)
|
70
|
-
rack-test (0.8.2)
|
71
|
-
rack (>= 1.0, < 3)
|
72
|
-
rails (5.1.4)
|
73
|
-
actioncable (= 5.1.4)
|
74
|
-
actionmailer (= 5.1.4)
|
75
|
-
actionpack (= 5.1.4)
|
76
|
-
actionview (= 5.1.4)
|
77
|
-
activejob (= 5.1.4)
|
78
|
-
activemodel (= 5.1.4)
|
79
|
-
activerecord (= 5.1.4)
|
80
|
-
activesupport (= 5.1.4)
|
81
|
-
bundler (>= 1.3.0)
|
82
|
-
railties (= 5.1.4)
|
83
|
-
sprockets-rails (>= 2.0.0)
|
84
|
-
rails-dom-testing (2.0.3)
|
85
|
-
activesupport (>= 4.2.0)
|
86
|
-
nokogiri (>= 1.6)
|
87
|
-
rails-html-sanitizer (1.0.3)
|
88
|
-
loofah (~> 2.0)
|
89
|
-
railties (5.1.4)
|
90
|
-
actionpack (= 5.1.4)
|
91
|
-
activesupport (= 5.1.4)
|
92
|
-
method_source
|
93
|
-
rake (>= 0.8.7)
|
94
|
-
thor (>= 0.18.1, < 2.0)
|
95
|
-
rake (12.3.0)
|
96
11
|
rspec (3.7.0)
|
97
12
|
rspec-core (~> 3.7.0)
|
98
13
|
rspec-expectations (~> 3.7.0)
|
@@ -106,20 +21,6 @@ GEM
|
|
106
21
|
diff-lcs (>= 1.2.0, < 2.0)
|
107
22
|
rspec-support (~> 3.7.0)
|
108
23
|
rspec-support (3.7.0)
|
109
|
-
sprockets (3.7.1)
|
110
|
-
concurrent-ruby (~> 1.0)
|
111
|
-
rack (> 1, < 3)
|
112
|
-
sprockets-rails (3.2.1)
|
113
|
-
actionpack (>= 4.0)
|
114
|
-
activesupport (>= 4.0)
|
115
|
-
sprockets (>= 3.0.0)
|
116
|
-
thor (0.20.0)
|
117
|
-
thread_safe (0.3.6)
|
118
|
-
tzinfo (1.2.4)
|
119
|
-
thread_safe (~> 0.1)
|
120
|
-
websocket-driver (0.6.5)
|
121
|
-
websocket-extensions (>= 0.1.0)
|
122
|
-
websocket-extensions (0.1.3)
|
123
24
|
|
124
25
|
PLATFORMS
|
125
26
|
ruby
|
data/README.md
CHANGED
@@ -9,23 +9,53 @@ _Warning: This beta gem is currently used for personal used. Most Keycloak Admin
|
|
9
9
|
## Install
|
10
10
|
|
11
11
|
This gem *does not* require Rails.
|
12
|
+
For example, using `bundle`, add this line to your Gemfile.
|
12
13
|
|
13
14
|
```ruby
|
14
|
-
gem "keycloak-admin"
|
15
|
+
gem "keycloak-admin", "0.2"
|
15
16
|
```
|
16
17
|
|
18
|
+
## Login
|
19
|
+
|
20
|
+
You can choose your login process between two different login methods: `username/password` and `Account Service`.
|
21
|
+
|
22
|
+
### Login with username/password
|
23
|
+
|
24
|
+
Using this login method requires to create a user (and her credentials).
|
25
|
+
* In Keycloak
|
26
|
+
* Make your client `confidential` or `public`
|
27
|
+
* Do not check `Service Accounts Enabled`
|
28
|
+
* In this gem's configuration
|
29
|
+
* Set `use_service_account` to `false`
|
30
|
+
* Setup `username` and `password`
|
31
|
+
* Setup `client_secret` if your client is `confidential`
|
32
|
+
|
33
|
+
### Login with an Account Service
|
34
|
+
|
35
|
+
Using a service account to use the REST Admin API does not require to create a dedicated user (http://www.keycloak.org/docs/2.5/server_admin/topics/clients/oidc/service-accounts.html).
|
36
|
+
|
37
|
+
* In Keycloak
|
38
|
+
* Make your client `confidential`
|
39
|
+
* Check its toggle `Service Accounts Enabled`
|
40
|
+
* A Redirect URL is required, set it to `*`
|
41
|
+
* After saving this client, open the `Service Account Roles` and add relevant `realm-management.` client's roles. For instance: `view-users` if you want to search for users using this gem.
|
42
|
+
* In this gem's configuration
|
43
|
+
* Set `use_service_account` to `true`
|
44
|
+
* Setup `client_secret`
|
45
|
+
|
17
46
|
## Configuration
|
18
47
|
|
19
48
|
To configure this gem, call `KeycloakAdmin.configure`.
|
20
49
|
For instance, to configure this gem based on environment variables, write (and load if required) a `keycloak_admin.rb`:
|
21
50
|
```ruby
|
22
51
|
KeycloakAdmin.configure do |config|
|
23
|
-
config.
|
24
|
-
config.
|
25
|
-
config.
|
26
|
-
config.
|
27
|
-
config.
|
28
|
-
config.
|
52
|
+
config.use_service_account = false
|
53
|
+
config.server_url = ENV["KEYCLOAK_SERVER_URL"]
|
54
|
+
config.client_id = ENV["KEYCLOAK_ADMIN_CLIENT_ID"]
|
55
|
+
config.client_realm_name = ENV["KEYCLOAK_REALM_ID"]
|
56
|
+
config.username = ENV["KEYCLOAK_ADMIN_USER"]
|
57
|
+
config.password = ENV["KEYCLOAK_ADMIN_PASSWORD"]
|
58
|
+
config.logger = Rails.logger
|
29
59
|
end
|
30
60
|
```
|
31
61
|
This example is autoloaded in a Rails environment.
|
@@ -37,10 +67,12 @@ All options have a default value. However, all of them can be changed in your in
|
|
37
67
|
| Option | Default Value | Type | Required? | Description | Example |
|
38
68
|
| ---- | ----- | ------ | ----- | ------ | ----- |
|
39
69
|
| `server_url` | `nil`| String | Required | The base url where your Keycloak server is located. This value can be retrieved in your Keycloak client configuration. | `auth:8080/auth` |
|
40
|
-
| `
|
70
|
+
| `client_realm_name` | `""`| String | Required | Name of the realm that contain the admin client. | `master` |
|
41
71
|
| `client_id` | `admin-cli`| String | Required | Client that should be used to access admin capabilities. | `api-cli` |
|
42
|
-
| `
|
43
|
-
| `
|
72
|
+
| `client_secret` | `nil`| String | Optional | If your client is `confidential`, this parameter must be specified. | `4e3c481c-f823-4a6a-b8a7-bf8c86e3eac3` |
|
73
|
+
| `use_service_account` | `true` | Boolean | Required | `true` if the connection to the client uses a Service Account. `false` if the connetio nto the client uses a username/password credential | `false` |
|
74
|
+
| `username` | `nil`| String | Optional | Username that access to the Admin REST API. Recommended if `user_service_account` is set to `false`. | `mummy` |
|
75
|
+
| `password` | `nil`| String | Optional | Clear password that access to the Admin REST API. Recommended if `user_service_account` is set to `false`. | `bobby` |
|
44
76
|
| `logger` | `Logger.new(STDOUT)`| Logger | Optional | The logger used by `keycloak-admin` | `Rails.logger` |
|
45
77
|
|
46
78
|
|
@@ -105,3 +137,7 @@ From the `keycloak-admin-api` directory:
|
|
105
137
|
$ docker build . -t keycloak-admin:test
|
106
138
|
$ docker run -v `pwd`:/usr/src/app/ keycloak-admin:test bundle exec rspec spec
|
107
139
|
```
|
140
|
+
|
141
|
+
## Future work
|
142
|
+
|
143
|
+
* Allow authentication using JWT assertions
|
data/keycloak-admin.gemspec
CHANGED
@@ -15,6 +15,6 @@ 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.add_development_dependency "rspec",
|
18
|
+
spec.add_development_dependency "rspec", "3.7.0"
|
19
19
|
spec.add_development_dependency "byebug", "9.1.0"
|
20
20
|
end
|
data/lib/keycloak-admin.rb
CHANGED
@@ -31,12 +31,13 @@ module KeycloakAdmin
|
|
31
31
|
|
32
32
|
def self.load_configuration
|
33
33
|
configure do |config|
|
34
|
-
config.server_url
|
35
|
-
config.
|
36
|
-
config.client_id
|
37
|
-
config.logger
|
38
|
-
config.
|
39
|
-
config.
|
34
|
+
config.server_url = nil
|
35
|
+
config.client_realm_name = ""
|
36
|
+
config.client_id = "admin-cli"
|
37
|
+
config.logger = ::Logger.new(STDOUT)
|
38
|
+
config.use_service_account = true
|
39
|
+
config.username = nil
|
40
|
+
config.password = nil
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
@@ -10,7 +10,7 @@ module KeycloakAdmin
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def token
|
13
|
-
@token ||= KeycloakAdmin.realm(@configuration.
|
13
|
+
@token ||= KeycloakAdmin.realm(@configuration.client_realm_name).token.get
|
14
14
|
end
|
15
15
|
|
16
16
|
def headers
|
@@ -21,9 +21,15 @@ module KeycloakAdmin
|
|
21
21
|
}
|
22
22
|
end
|
23
23
|
|
24
|
+
def execute_http
|
25
|
+
yield
|
26
|
+
rescue RestClient::ExceptionWithResponse => e
|
27
|
+
http_error(e.response)
|
28
|
+
end
|
29
|
+
|
24
30
|
private
|
25
31
|
|
26
|
-
def
|
32
|
+
def http_error(response)
|
27
33
|
raise "Keycloak: The request failed with response code #{response.code} and message: #{response.body}"
|
28
34
|
end
|
29
35
|
end
|
@@ -15,17 +15,10 @@ module KeycloakAdmin
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def get
|
18
|
-
response =
|
19
|
-
|
20
|
-
password: @configuration.password,
|
21
|
-
grant_type: "password",
|
22
|
-
client_id: @configuration.client_id
|
23
|
-
)
|
24
|
-
if response.code == 200
|
25
|
-
TokenRepresentation.from_json(response.body)
|
26
|
-
else
|
27
|
-
error(response)
|
18
|
+
response = execute_http do
|
19
|
+
RestClient.post(token_url, @configuration.body_for_token_retrieval, @configuration.headers_for_token_retrieval)
|
28
20
|
end
|
21
|
+
TokenRepresentation.from_json(response.body)
|
29
22
|
end
|
30
23
|
end
|
31
24
|
end
|
@@ -12,39 +12,35 @@ module KeycloakAdmin
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def save(user_representation)
|
15
|
-
|
16
|
-
|
17
|
-
user_representation
|
18
|
-
else
|
19
|
-
error(response)
|
15
|
+
execute_http do
|
16
|
+
RestClient.post(users_url, user_representation.to_json, headers)
|
20
17
|
end
|
18
|
+
user_representation
|
21
19
|
end
|
22
20
|
|
23
21
|
def search(query)
|
24
|
-
response =
|
25
|
-
|
26
|
-
JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
|
27
|
-
else
|
28
|
-
error(response)
|
22
|
+
response = execute_http do
|
23
|
+
RestClient.get(users_url, headers.merge({params: { search: query }}))
|
29
24
|
end
|
25
|
+
JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
|
30
26
|
end
|
31
27
|
|
32
28
|
def delete(user_id)
|
33
|
-
|
34
|
-
|
29
|
+
execute_http do
|
30
|
+
RestClient.delete(users_url(user_id), headers)
|
31
|
+
end
|
32
|
+
true
|
35
33
|
end
|
36
34
|
|
37
35
|
def update_password(user_id, new_password)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
user_id
|
45
|
-
else
|
46
|
-
error(response)
|
36
|
+
execute_http do
|
37
|
+
RestClient.put(reset_password_url(user_id), {
|
38
|
+
type: "password",
|
39
|
+
value: new_password,
|
40
|
+
temporary: false
|
41
|
+
}.to_json, headers)
|
47
42
|
end
|
43
|
+
user_id
|
48
44
|
end
|
49
45
|
|
50
46
|
def users_url(id=nil)
|
@@ -1,5 +1,52 @@
|
|
1
|
+
require "base64"
|
2
|
+
|
1
3
|
module KeycloakAdmin
|
2
4
|
class Configuration
|
3
|
-
attr_accessor :server_url, :client_id, :
|
5
|
+
attr_accessor :server_url, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger
|
6
|
+
|
7
|
+
def body_for_token_retrieval
|
8
|
+
if use_service_account
|
9
|
+
body_for_service_account
|
10
|
+
else
|
11
|
+
body_for_username_and_password
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def headers_for_token_retrieval
|
16
|
+
if use_service_account
|
17
|
+
headers_for_service_account
|
18
|
+
else
|
19
|
+
headers_for_username_and_password
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def body_for_service_account
|
26
|
+
{
|
27
|
+
grant_type: "client_credentials"
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def body_for_username_and_password
|
32
|
+
{
|
33
|
+
username: username,
|
34
|
+
password: password,
|
35
|
+
grant_type: "password",
|
36
|
+
client_id: client_id,
|
37
|
+
client_secret: client_secret
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def headers_for_service_account
|
42
|
+
id_and_secret = Base64::strict_encode64("#{client_id}:#{client_secret}")
|
43
|
+
{
|
44
|
+
Authorization: "Basic #{id_and_secret}"
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def headers_for_username_and_password
|
49
|
+
{}
|
50
|
+
end
|
4
51
|
end
|
5
|
-
end
|
52
|
+
end
|
@@ -1,14 +1,39 @@
|
|
1
1
|
|
2
2
|
module KeycloakAdmin
|
3
3
|
class TokenRepresentation < Representation
|
4
|
-
attr_accessor :access_token
|
4
|
+
attr_accessor :access_token,
|
5
|
+
:token_type,
|
6
|
+
:expires_in,
|
7
|
+
:refresh_token,
|
8
|
+
:refresh_expires_in,
|
9
|
+
:id_token,
|
10
|
+
:not_before_policy,
|
11
|
+
:session_state
|
5
12
|
|
6
|
-
def initialize(access_token)
|
7
|
-
@access_token
|
13
|
+
def initialize(access_token, token_type, expires_in, refresh_token, refresh_expires_in, id_token, not_before_policy, session_state)
|
14
|
+
@access_token = access_token
|
15
|
+
@token_type = token_type
|
16
|
+
@expires_in = expires_in
|
17
|
+
@refresh_token = refresh_token
|
18
|
+
@refresh_expires_in = refresh_expires_in
|
19
|
+
@id_token = id_token
|
20
|
+
@not_before_policy = not_before_policy
|
21
|
+
@session_state = session_state
|
8
22
|
end
|
9
23
|
|
10
24
|
def self.from_hash(hash)
|
11
|
-
new(
|
25
|
+
new(
|
26
|
+
hash["access_token"],
|
27
|
+
hash["token_type"],
|
28
|
+
hash["expires_in"],
|
29
|
+
hash["refresh_token"],
|
30
|
+
hash["refresh_expires_in"],
|
31
|
+
hash["id_token"],
|
32
|
+
hash["not-before-policy"],
|
33
|
+
hash["session_state"],
|
34
|
+
)
|
12
35
|
end
|
13
36
|
end
|
14
37
|
end
|
38
|
+
|
39
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -4,11 +4,11 @@ require "byebug"
|
|
4
4
|
|
5
5
|
def configure
|
6
6
|
KeycloakAdmin.configure do |config|
|
7
|
-
config.server_url
|
8
|
-
config.client_id
|
9
|
-
config.
|
10
|
-
config.
|
11
|
-
config.
|
7
|
+
config.server_url = "http://auth.service.io/auth"
|
8
|
+
config.client_id = "admin-cli"
|
9
|
+
config.client_secret = "aaaaaaaa"
|
10
|
+
config.client_realm_name = "master2"
|
11
|
+
config.use_service_account = true
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|