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 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