keycloak-admin 0.7.3 → 0.7.8

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
- SHA1:
3
- metadata.gz: 8dc193e7b260cc860b03a8eaaa53180fc6944e71
4
- data.tar.gz: 229097dbd02866fa93fa51b834b39dcfba7a97a9
2
+ SHA256:
3
+ metadata.gz: b7d17b8a5f55e87b615da11c9554bfeec127629207e81bd4e4950fb85971010c
4
+ data.tar.gz: '097affd8e18b7e778557fe4ecc787e93127bcef133a4dd9e6db590fc7ad65982'
5
5
  SHA512:
6
- metadata.gz: 084c52d8b2cdd9a38d68f01c1bba6db6ab6d0735efdef1a6d61af31fb248a1ce6d72d2b50e5bf8487edb60d6846d3b15fbdcb3e850202b5413632a26b83d1c50
7
- data.tar.gz: 588b84ec13efd6538998bba3d0d7422bf3abdd03525f40e0860e84bc675afd7acd0856f0876f44d11cdb0c5bd2e1528df3c76e6b861ffbfa8c01e7f973d0571d
6
+ metadata.gz: ffc55b0f6c7719ce1fa38b93838fe2d137fe3cd6395fe1717ac4ef352484bdfedab0363811f88bf9e82815d1ae6f94ec008ab1fb4db5038f638cc816d66362fd
7
+ data.tar.gz: b8053bb57699928d793523b9d0274a284ebafbedd8e05d8294fbcdebcca967d69531dddf2048fd890b8e51dc3bb9bea2c83a9aee19e34bac13fccbed95a547b7
@@ -5,9 +5,33 @@ 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
+ ## [0.7.8] - 2020-10-15
9
+
10
+ * Bug: `rest_client_options` default value does not match the documentation (was `nil` by default, should be `{}`)
11
+ * Update documentation about client setup (based on Keycloak 11)
12
+
13
+ ## [0.7.7] - 2020-07-10
14
+
15
+ * Fix: `Replace request method shorthand with .execute for proper RestClient option support` (thanks to @RomanHargrave)
16
+ * When sending action emails, add lifespan as an optional parameter (thanks to @hobbypunk90)
17
+
18
+ ## [0.7.6] - 2020-06-22
19
+
20
+ Thanks to @hobbypunk90
21
+ * Support for action emails and send forgot passsword mail
22
+
23
+ ## [0.7.5] - 2020-03-28
24
+
25
+ Thanks to @RomanHargrave
26
+ * Support for working with federated identity provider (broker) links
27
+
28
+ ## [0.7.4] - 2019-10-17
29
+
30
+ * Support for Rails 6
31
+
8
32
  ## [0.7.3] - 2019-07-11
9
33
 
10
- Thanks to @cederigo=
34
+ Thanks to @cederigo:
11
35
  * For a given user, get her list of groups
12
36
 
13
37
  ## [0.7.2] - 2019-06-17
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:2.3
1
+ FROM ruby:2.6.5
2
2
  RUN mkdir -p /usr/src/app/lib/keycloak-admin
3
3
  WORKDIR /usr/src/app
4
4
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- keycloak-admin (0.7.3)
4
+ keycloak-admin (0.7.8)
5
5
  http-cookie (~> 1.0, >= 1.0.3)
6
6
  rest-client (~> 2.0)
7
7
 
@@ -12,13 +12,15 @@ GEM
12
12
  diff-lcs (1.3)
13
13
  domain_name (0.5.20190701)
14
14
  unf (>= 0.0.5, < 1.0.0)
15
+ http-accept (1.7.0)
15
16
  http-cookie (1.0.3)
16
17
  domain_name (~> 0.5)
17
- mime-types (3.2.2)
18
+ mime-types (3.3.1)
18
19
  mime-types-data (~> 3.2015)
19
- mime-types-data (3.2019.0331)
20
+ mime-types-data (3.2020.0512)
20
21
  netrc (0.11.0)
21
- rest-client (2.0.2)
22
+ rest-client (2.1.0)
23
+ http-accept (>= 1.7.0, < 2.0)
22
24
  http-cookie (>= 1.0.2, < 2.0)
23
25
  mime-types (>= 1.16, < 4.0)
24
26
  netrc (~> 0.8)
@@ -37,7 +39,7 @@ GEM
37
39
  rspec-support (3.7.0)
38
40
  unf (0.1.4)
39
41
  unf_ext
40
- unf_ext (0.0.7.6)
42
+ unf_ext (0.0.7.7)
41
43
 
42
44
  PLATFORMS
43
45
  ruby
data/README.md CHANGED
@@ -12,37 +12,59 @@ This gem *does not* require Rails.
12
12
  For example, using `bundle`, add this line to your Gemfile.
13
13
 
14
14
  ```ruby
15
- gem "keycloak-admin", "0.7.3"
15
+ gem "keycloak-admin", "0.7.8"
16
16
  ```
17
17
 
18
18
  ## Login
19
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
- * Disable both `Standard Flow Enabled` and `Implicit Flow Enabled `
41
- * Enable `Direct Access Grants Enabled`
42
- * 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.
43
- * In this gem's configuration
20
+ To login on Keycloak's Admin API, you first need to setup a client.
21
+
22
+ Go to your realm administration page and open `Clients`. Then, click on the `Create` button.
23
+ On the first screen, enter:
24
+ * `Client ID`: _e.g. my-app-admin-client_
25
+ * `Client Protocol`: select `openid-connect`
26
+ * `Root URL`: let it blank
27
+
28
+ The next screen must be configured depending on how you want to authenticate:
29
+ * `username/password` with a user of the realm
30
+ * `Direct Access Grants` with a service account
31
+
32
+ ### Login with username/password (realm user)
33
+
34
+ * In Keycloak, during the client setup:
35
+ * `Access Type`: `public` or `confidential`
36
+ * `Service Accounts Enabled` (when `confidential`): `false`
37
+ * After saving your client, if you have chosen a `confidential` client, go to `Credentials` tab and copy the `Client Secret`
38
+
39
+ * In Keycloak, create a dedicated user (and her credentials):
40
+ * Go to `Users`
41
+ * Click on the `Add user` button
42
+ * Setup her mandatory information, depending on your realm's configuration
43
+ * On the `Credentials` tab, create her a password (toggle off `Temporary`)
44
+
45
+ * In this gem's configuration (see Section `Configuration`):
46
+ * Setup `username` and `password` according to your user's configuration
47
+ * Setup `client_id` with your `Client ID` (_e.g. my-app-admin-client_)
48
+ * If your client is `confidential`, copy its Client Secret to `client_secret`
49
+
50
+ ### Login with `Direct Access Grants` (Service account)
51
+
52
+ Using a service account to use the REST Admin API does not require to create a dedicated user (https://www.keycloak.org/docs/latest/server_admin/#_service_accounts).
53
+
54
+ * In Keycloak, during the client setup:
55
+ * `Access Type`: `confidential`
56
+ * `Service Accounts Enabled` (when `confidential`): `true`
57
+ * `Standard Flow Enabled`: `false`
58
+ * `Implicit Flow Enabled`: `false`
59
+ * `Direct Access Grants Enabled`: `true`
60
+ * After saving this client
61
+ * 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.
62
+ * open the `Credentials` tab and copy the `Client Secret`
63
+
64
+ * In this gem's configuration (see Section `Configuration`):
44
65
  * Set `use_service_account` to `true`
45
- * Setup `client_secret`
66
+ * Setup `client_id` with your `Client ID` (_e.g. my-app-admin-client_)
67
+ * Copy its Client Secret to `client_secret`
46
68
 
47
69
  ## Configuration
48
70
 
@@ -97,6 +119,9 @@ All options have a default value. However, all of them can be changed in your in
97
119
  * Get list of client role mappings for a user/group
98
120
  * Save client role mappings for a user/group
99
121
  * Save realm-level role mappings for a user/group
122
+ * Link/Unlink users to federated identity provider brokers
123
+ * Execute actions emails
124
+ * Send forgot passsword mail
100
125
 
101
126
  ### Get an access token
102
127
 
@@ -340,8 +365,4 @@ From the `keycloak-admin-api` directory:
340
365
  ```
341
366
  $ docker build . -t keycloak-admin:test
342
367
  $ docker run -v `pwd`:/usr/src/app/ keycloak-admin:test bundle exec rspec spec
343
- ```
344
-
345
- ## Future work
346
-
347
- * Allow authentication using JWT assertions
368
+ ```
@@ -21,6 +21,7 @@ require_relative "keycloak-admin/representation/impersonation_representation"
21
21
  require_relative "keycloak-admin/representation/credential_representation"
22
22
  require_relative "keycloak-admin/representation/realm_representation"
23
23
  require_relative "keycloak-admin/representation/role_representation"
24
+ require_relative "keycloak-admin/representation/federated_identity_representation"
24
25
  require_relative "keycloak-admin/representation/user_representation"
25
26
  require_relative "keycloak-admin/resource/base_role_containing_resource"
26
27
  require_relative "keycloak-admin/resource/group_resource"
@@ -54,7 +55,7 @@ module KeycloakAdmin
54
55
  config.use_service_account = true
55
56
  config.username = nil
56
57
  config.password = nil
57
- config.rest_client_options = nil
58
+ config.rest_client_options = {}
58
59
  end
59
60
  end
60
61
 
@@ -16,13 +16,18 @@ module KeycloakAdmin
16
16
 
17
17
  def exchange_with(user_access_token, token_lifespan_in_seconds)
18
18
  response = execute_http do
19
- RestClient.post(token_url, {
20
- tokenLifespanInSeconds: token_lifespan_in_seconds
21
- }.to_json, {
22
- Authorization: "Bearer #{user_access_token}",
23
- content_type: :json,
24
- accept: :json
25
- })
19
+ RestClient::Request.execute(
20
+ @configuration.rest_client_options.merge(
21
+ method: :post,
22
+ url: token_url,
23
+ payload: { tokenLifespanInSeconds: token_lifespan_in_seconds }.to_json,
24
+ headers: {
25
+ Authorization: "Bearer #{user_access_token}",
26
+ content_type: :json,
27
+ accept: :json
28
+ }
29
+ )
30
+ )
26
31
  end
27
32
  TokenRepresentation.from_json(response.body)
28
33
  end
@@ -21,7 +21,14 @@ module KeycloakAdmin
21
21
  end
22
22
 
23
23
  def update(user_id, user_representation_body)
24
- RestClient.put(users_url(user_id), user_representation_body.to_json, headers)
24
+ RestClient::Request.execute(
25
+ @configuration.rest_client_options.merge(
26
+ method: :put,
27
+ url: users_url(user_id),
28
+ payload: user_representation_body.to_json,
29
+ headers: headers
30
+ )
31
+ )
25
32
  end
26
33
 
27
34
  def get(user_id)
@@ -59,11 +66,26 @@ module KeycloakAdmin
59
66
 
60
67
  def update_password(user_id, new_password)
61
68
  execute_http do
62
- RestClient.put(reset_password_url(user_id), {
63
- type: "password",
64
- value: new_password,
65
- temporary: false
66
- }.to_json, headers)
69
+ RestClient::Request.execute(
70
+ @configuration.rest_client_options.merge(
71
+ method: :put,
72
+ url: reset_password_url(user_id),
73
+ payload: { type: 'password', value: new_password, temporary: false }.to_json,
74
+ headers: headers
75
+ )
76
+ )
77
+ end
78
+ user_id
79
+ end
80
+
81
+ def forgot_password(user_id, lifespan=nil)
82
+ execute_actions_email(user_id, ["UPDATE_PASSWORD"], lifespan)
83
+ end
84
+
85
+ def execute_actions_email(user_id, actions=[], lifespan=nil)
86
+ execute_http do
87
+ lifespan_param = lifespan.nil? ? "" : "lifespan=#{lifespan.seconds}"
88
+ RestClient.put("#{execute_actions_email_url(user_id)}?#{lifespan_param}", actions.to_json, headers)
67
89
  end
68
90
  user_id
69
91
  end
@@ -71,7 +93,14 @@ module KeycloakAdmin
71
93
  def impersonate(user_id)
72
94
  impersonation = get_redirect_impersonation(user_id)
73
95
  response = execute_http do
74
- RestClient.post(impersonation.impersonation_url, impersonation.body.to_json, impersonation.headers)
96
+ RestClient::Request.execute(
97
+ @configuration.rest_client_options.merge(
98
+ method: :post,
99
+ url: impersonation.impersonation_url,
100
+ payload: impersonation.body.to_json,
101
+ headers: impersonation.headers
102
+ )
103
+ )
75
104
  end
76
105
  ImpersonationRepresentation.from_response(response, @configuration.server_domain)
77
106
  end
@@ -80,6 +109,30 @@ module KeycloakAdmin
80
109
  ImpersonationRedirectionRepresentation.from_url(impersonation_url(user_id), headers)
81
110
  end
82
111
 
112
+ def link_idp(user_id, idp_id, idp_user_id, idp_username)
113
+ fed_id_rep = FederatedIdentityRepresentation.new
114
+ fed_id_rep.user_id = idp_user_id
115
+ fed_id_rep.user_name = idp_username
116
+ fed_id_rep.identity_provider = idp_id
117
+
118
+ execute_http do
119
+ RestClient::Request.execute(
120
+ @configuration.rest_client_options.merge(
121
+ method: :post,
122
+ url: federated_identity_url(user_id, idp_id),
123
+ payload: fed_id_rep.to_json,
124
+ headers: headers
125
+ )
126
+ )
127
+ end
128
+ end
129
+
130
+ def unlink_idp(user_id, idp_id)
131
+ execute_http do
132
+ RestClient::Resource.new(federated_identity_url(user_id, idp_id), @configuration.rest_client_options).delete(headers)
133
+ end
134
+ end
135
+
83
136
  def users_url(id=nil)
84
137
  if id
85
138
  "#{@realm_client.realm_admin_url}/users/#{id}"
@@ -93,6 +146,11 @@ module KeycloakAdmin
93
146
  "#{users_url(user_id)}/reset-password"
94
147
  end
95
148
 
149
+ def execute_actions_email_url(user_id)
150
+ raise ArgumentError.new("user_id must be defined") if user_id.nil?
151
+ "#{users_url(user_id)}/execute-actions-email"
152
+ end
153
+
96
154
  def groups_url(user_id)
97
155
  raise ArgumentError.new("user_id must be defined") if user_id.nil?
98
156
  "#{users_url(user_id)}/groups"
@@ -103,6 +161,12 @@ module KeycloakAdmin
103
161
  "#{users_url(user_id)}/impersonation"
104
162
  end
105
163
 
164
+ def federated_identity_url(user_id, identity_provider)
165
+ raise ArgumentError.new("user_id must be defined") if user_id.nil?
166
+ raise ArgumentError.new("identity_provider must be defined") if identity_provider.nil?
167
+ "#{users_url(user_id)}/federated-identity/#{identity_provider}"
168
+ end
169
+
106
170
  private
107
171
 
108
172
  def build(username, email, password, email_verified, locale)
@@ -0,0 +1,15 @@
1
+ module KeycloakAdmin
2
+ class FederatedIdentityRepresentation < Representation
3
+ attr_accessor :identity_provider,
4
+ :user_id,
5
+ :user_name
6
+
7
+ def self.from_hash(hash)
8
+ rep = new
9
+ rep.identity_provider = hash['identityProvider']
10
+ rep.user_id = hash['userId']
11
+ rep.user_name = hash['userName']
12
+ rep
13
+ end
14
+ end
15
+ end
@@ -4,12 +4,12 @@ require_relative "camel_json"
4
4
  class Representation
5
5
  include ::KeycloakAdmin::CamelJson
6
6
 
7
- def as_json
7
+ def as_json(options=nil)
8
8
  Hash[instance_variables.map { |ivar| [ivar.to_s[1..-1], instance_variable_get(ivar)] }]
9
9
  end
10
10
 
11
11
  def to_json(options=nil)
12
- snaked_hash = as_json
12
+ snaked_hash = as_json(options)
13
13
  snaked_hash.keys.reduce({}) do |camelized_hash, key|
14
14
  camelized_hash[camelize(key, false)] = snaked_hash[key]
15
15
  camelized_hash
@@ -10,21 +10,23 @@ module KeycloakAdmin
10
10
  :email_verified,
11
11
  :first_name,
12
12
  :last_name,
13
- :credentials
13
+ :credentials,
14
+ :federated_identities
14
15
 
15
16
  def self.from_hash(hash)
16
- user = new
17
- user.id = hash["id"]
18
- user.created_timestamp = hash["createdTimestamp"]
19
- user.origin = hash["origin"]
20
- user.username = hash["username"]
21
- user.email = hash["email"]
22
- user.enabled = hash["enabled"]
23
- user.email_verified = hash["emailVerified"]
24
- user.first_name = hash["firstName"]
25
- user.last_name = hash["lastName"]
26
- user.attributes = hash["attributes"]
27
- user.credentials = hash["credentials"]&.map{ |hash| CredentialRepresentation.from_hash(hash) } || []
17
+ user = new
18
+ user.id = hash["id"]
19
+ user.created_timestamp = hash["createdTimestamp"]
20
+ user.origin = hash["origin"]
21
+ user.username = hash["username"]
22
+ user.email = hash["email"]
23
+ user.enabled = hash["enabled"]
24
+ user.email_verified = hash["emailVerified"]
25
+ user.first_name = hash["firstName"]
26
+ user.last_name = hash["lastName"]
27
+ user.attributes = hash["attributes"]
28
+ user.credentials = hash["credentials"]&.map{ |hash| CredentialRepresentation.from_hash(hash) } || []
29
+ user.federated_identities = hash["federatedIdentities"]&.map { |hash| FederatedIdentityRepresentation.from_hash(hash) } || []
28
30
  user
29
31
  end
30
32
 
@@ -32,5 +34,10 @@ module KeycloakAdmin
32
34
  @credentials ||= []
33
35
  @credentials.push(credential_representation)
34
36
  end
37
+
38
+ def add_federated_identity(federated_identity_representation)
39
+ @federated_identities ||= []
40
+ @federated_identities.push(federated_identity_representation)
41
+ end
35
42
  end
36
43
  end
@@ -1,3 +1,3 @@
1
1
  module KeycloakAdmin
2
- VERSION = "0.7.3"
2
+ VERSION = "0.7.8"
3
3
  end
@@ -72,6 +72,31 @@ RSpec.describe KeycloakAdmin::TokenClient do
72
72
  end
73
73
  end
74
74
 
75
+ describe "#execute_actions_email_url" do
76
+ let(:realm_name) { "valid-realm" }
77
+ let(:user_id) { nil }
78
+
79
+ before(:each) do
80
+ @client = KeycloakAdmin.realm(realm_name).users
81
+ end
82
+
83
+ context "when user_id is not defined" do
84
+ let(:user_id) { nil }
85
+ it "raises an error" do
86
+ expect {
87
+ @client.execute_actions_email_url(user_id)
88
+ }.to raise_error(ArgumentError)
89
+ end
90
+ end
91
+
92
+ context "when user_id is defined" do
93
+ let(:user_id) { 42 }
94
+ it "return a proper url" do
95
+ expect(@client.execute_actions_email_url(user_id)).to eq "http://auth.service.io/auth/admin/realms/valid-realm/users/42/execute-actions-email"
96
+ end
97
+ end
98
+ end
99
+
75
100
  describe "#impersonation_url" do
76
101
  let(:realm_name) { "valid-realm" }
77
102
  let(:user_id) { nil }
@@ -9,7 +9,7 @@ RSpec.describe KeycloakAdmin::UserRepresentation do
9
9
  end
10
10
 
11
11
  it "can convert to json" do
12
- expect(@user.to_json).to eq '{"id":null,"createdTimestamp":1559836000,"origin":null,"username":"test_username","email":null,"enabled":true,"emailVerified":null,"firstName":null,"lastName":null,"attributes":null,"credentials":[]}'
12
+ expect(@user.to_json).to eq '{"id":null,"createdTimestamp":1559836000,"origin":null,"username":"test_username","email":null,"enabled":true,"emailVerified":null,"firstName":null,"lastName":null,"attributes":null,"credentials":[],"federatedIdentities":[]}'
13
13
  end
14
14
  end
15
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keycloak-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorent Lempereur
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-11 00:00:00.000000000 Z
11
+ date: 2020-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-cookie
@@ -103,6 +103,7 @@ files:
103
103
  - lib/keycloak-admin/representation/camel_json.rb
104
104
  - lib/keycloak-admin/representation/client_representation.rb
105
105
  - lib/keycloak-admin/representation/credential_representation.rb
106
+ - lib/keycloak-admin/representation/federated_identity_representation.rb
106
107
  - lib/keycloak-admin/representation/group_representation.rb
107
108
  - lib/keycloak-admin/representation/impersonation_redirection_representation.rb
108
109
  - lib/keycloak-admin/representation/impersonation_representation.rb
@@ -150,8 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
151
  - !ruby/object:Gem::Version
151
152
  version: '0'
152
153
  requirements: []
153
- rubyforge_project:
154
- rubygems_version: 2.6.4
154
+ rubygems_version: 3.0.3
155
155
  signing_key:
156
156
  specification_version: 4
157
157
  summary: Keycloak Admin REST API client written in Ruby