keycloak-admin 0.1 → 0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 107fe1a9772c1ca9f9014e1e089d5eb4a9c66a6a
4
- data.tar.gz: f4c5918e8e3f3654bb6f8e8f63d0480b2f7b5d98
3
+ metadata.gz: 530efac24194a86644d0924ffd5a0aa931c6928d
4
+ data.tar.gz: 9e42d8ceed9f876549dbd2cf2fd86c12ad01c500
5
5
  SHA512:
6
- metadata.gz: c5be1ffcad5a514578de150dae7a6b55d1ca7af1466fe3f45ec007c17da164ea877b190357fa50227f1e68ac3861a3daea793389cd2044b31ef9c5c0f1e30aa9
7
- data.tar.gz: 40932b69af9e0ba619a3d267cd50bbf0146f4a5573aee005e9d362c57e2536f39c4ba77320cbb0b0323cb37b9a07e1a347a05919da040cead84022368f1daf4c
6
+ metadata.gz: 6586f5fbd3b14cc579f8d77ff8cb8ee963eb013da1bd5d3807c6cfabe366b6b8340356c8d8cbc0207d4bf8eefaf8964a0717e0a25145d679b23d427f46641683
7
+ data.tar.gz: 4891b3ad7ffc41969c8e9a7b3624315f57edef8f9253dea0218c195c5a3eec1b019356ca2d75e0e1cc180ccb731f3ce7e47534335ea1ed915dcee63003604767
data/.gitignore CHANGED
@@ -5,3 +5,4 @@ test/dummy/db/*.sqlite3
5
5
  test/dummy/db/*.sqlite3-journal
6
6
  test/dummy/log/*.log
7
7
  test/dummy/tmp/
8
+ *.gem
@@ -1,98 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keycloak-admin (0.1)
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.server_url = ENV["KEYCLOAK_SERVER_URL"]
24
- config.client_id = ENV["KEYCLOAK_ADMIN_CLIENT_ID"]
25
- config.user_realm_name = ENV["KEYCLOAK_REALM_ID"]
26
- config.username = ENV["KEYCLOAK_ADMIN_USER"]
27
- config.password = ENV["KEYCLOAK_ADMIN_PASSWORD"]
28
- config.logger = Rails.logger
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
- | `user_realm_name` | `""`| String | Required | Name of the realm that contain the admin client and user. | `master` |
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
- | `username` | `nil`| String | Required | Username that access to the Admin REST API | `mummy` |
43
- | `password` | `nil`| String | Required | Clear password that access to the Admin REST API | `bobby` |
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
@@ -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", "3.7.0"
18
+ spec.add_development_dependency "rspec", "3.7.0"
19
19
  spec.add_development_dependency "byebug", "9.1.0"
20
20
  end
@@ -31,12 +31,13 @@ module KeycloakAdmin
31
31
 
32
32
  def self.load_configuration
33
33
  configure do |config|
34
- config.server_url = nil
35
- config.user_realm_name = ""
36
- config.client_id = "admin-cli"
37
- config.logger = ::Logger.new(STDOUT)
38
- config.username = nil
39
- config.password = nil
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.user_realm_name).token.get
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 error(response)
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 = RestClient.post(token_url,
19
- username: @configuration.username,
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
- response = RestClient.post(users_url, user_representation.to_json, headers)
16
- if response.code == 201
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 = RestClient.get(users_url, headers.merge({params: { search: query }}))
25
- if response.code == 200
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
- response = RestClient.delete(users_url(user_id), headers)
34
- response.code == 204 || error(response)
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
- response = RestClient.put(reset_password_url(user_id), {
39
- type: "password",
40
- value: new_password,
41
- temporary: false
42
- }.to_json, headers)
43
- if response.code == 204
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, :user_realm_name, :username, :password, :logger
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 = 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(hash["access_token"])
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
+
@@ -1,3 +1,3 @@
1
1
  module KeycloakAdmin
2
- VERSION = "0.1"
2
+ VERSION = "0.2"
3
3
  end
@@ -4,11 +4,11 @@ require "byebug"
4
4
 
5
5
  def configure
6
6
  KeycloakAdmin.configure do |config|
7
- config.server_url = "http://auth.service.io/auth"
8
- config.client_id = "admin-cli"
9
- config.user_realm_name = "master2"
10
- config.username = "bobby"
11
- config.password = "coucou"
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keycloak-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorent Lempereur