keycloak-admin 0.7.2 → 0.7.7

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: ef2112e82d91e64a41f612eb13dd37bbd6e7b09b
4
- data.tar.gz: 66218b9a1051a9d592d236235ddb1ac6e22161cf
2
+ SHA256:
3
+ metadata.gz: 8be78b3854a7d3ac95ddb700599f49f8b108f2eda85de66df200aa5e362b7f5e
4
+ data.tar.gz: 81103f2021f10e78c92327d7beace0ea30bc23175e3605c408e70f01a172203b
5
5
  SHA512:
6
- metadata.gz: 8034f148b70ded3b877a486865eadec656bb6a551cc71630d326aa3f81c4bbf2ed87541e6aa7ec5528934cad3d1dab65cc9dbf8adf747e3bf9f5b61531b8a01c
7
- data.tar.gz: cd4abc841445b3e73b4ee385b2e0a762c27a927d02a6814752929650fe6ccc37e06c8ec8e1b73552ede40f1e1652571032ed0ede0fc107ca3eec49d69dcd1e36
6
+ metadata.gz: 83bda7a2b12cd6384710234618392aebfe0b29aa61378bf8350c8c8b3c70ad9725293f150c0074137351b21825cd3798d6ca180180a4b5d9c84909bef67a32b3
7
+ data.tar.gz: c052198198d30a8978229dfc71d2d5cb0d26db94e22291e6286dea7d8133b2060d5d658f26fc5e7e5f4b9b35fc11f3dce0eff13c1451aa48e85ee2678e90fde6
@@ -5,6 +5,30 @@ 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.7] - 2020-07-10
9
+
10
+ * Fix: `Replace request method shorthand with .execute for proper RestClient option support` (thanks to @RomanHargrave)
11
+ * When sending action emails, add lifespan as an optional parameter (thanks to @hobbypunk90)
12
+
13
+ ## [0.7.6] - 2020-06-22
14
+
15
+ Thanks to @hobbypunk90
16
+ * Support for action emails and send forgot passsword mail
17
+
18
+ ## [0.7.5] - 2020-03-28
19
+
20
+ Thanks to @RomanHargrave
21
+ * Support for working with federated identity provider (broker) links
22
+
23
+ ## [0.7.4] - 2019-10-17
24
+
25
+ * Support for Rails 6
26
+
27
+ ## [0.7.3] - 2019-07-11
28
+
29
+ Thanks to @cederigo:
30
+ * For a given user, get her list of groups
31
+
8
32
  ## [0.7.2] - 2019-06-17
9
33
 
10
34
  Thanks to @vlad-ro:
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.2)
4
+ keycloak-admin (0.7.7)
5
5
  http-cookie (~> 1.0, >= 1.0.3)
6
6
  rest-client (~> 2.0)
7
7
 
@@ -10,15 +10,17 @@ GEM
10
10
  specs:
11
11
  byebug (9.1.0)
12
12
  diff-lcs (1.3)
13
- domain_name (0.5.20180417)
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,7 +12,7 @@ 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.2"
15
+ gem "keycloak-admin", "0.7.7"
16
16
  ```
17
17
 
18
18
  ## Login
@@ -97,6 +97,9 @@ All options have a default value. However, all of them can be changed in your in
97
97
  * Get list of client role mappings for a user/group
98
98
  * Save client role mappings for a user/group
99
99
  * Save realm-level role mappings for a user/group
100
+ * Link/Unlink users to federated identity provider brokers
101
+ * Execute actions emails
102
+ * Send forgot passsword mail
100
103
 
101
104
  ### Get an access token
102
105
 
@@ -340,8 +343,4 @@ From the `keycloak-admin-api` directory:
340
343
  ```
341
344
  $ docker build . -t keycloak-admin:test
342
345
  $ 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
346
+ ```
@@ -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"
@@ -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)
@@ -50,13 +57,35 @@ module KeycloakAdmin
50
57
  true
51
58
  end
52
59
 
60
+ def groups(user_id)
61
+ response = execute_http do
62
+ RestClient::Resource.new(groups_url(user_id), @configuration.rest_client_options).get(headers)
63
+ end
64
+ JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
65
+ end
66
+
53
67
  def update_password(user_id, new_password)
54
68
  execute_http do
55
- RestClient.put(reset_password_url(user_id), {
56
- type: "password",
57
- value: new_password,
58
- temporary: false
59
- }.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)
60
89
  end
61
90
  user_id
62
91
  end
@@ -64,7 +93,14 @@ module KeycloakAdmin
64
93
  def impersonate(user_id)
65
94
  impersonation = get_redirect_impersonation(user_id)
66
95
  response = execute_http do
67
- 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
+ )
68
104
  end
69
105
  ImpersonationRepresentation.from_response(response, @configuration.server_domain)
70
106
  end
@@ -73,6 +109,30 @@ module KeycloakAdmin
73
109
  ImpersonationRedirectionRepresentation.from_url(impersonation_url(user_id), headers)
74
110
  end
75
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
+
76
136
  def users_url(id=nil)
77
137
  if id
78
138
  "#{@realm_client.realm_admin_url}/users/#{id}"
@@ -86,11 +146,27 @@ module KeycloakAdmin
86
146
  "#{users_url(user_id)}/reset-password"
87
147
  end
88
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
+
154
+ def groups_url(user_id)
155
+ raise ArgumentError.new("user_id must be defined") if user_id.nil?
156
+ "#{users_url(user_id)}/groups"
157
+ end
158
+
89
159
  def impersonation_url(user_id)
90
160
  raise ArgumentError.new("user_id must be defined") if user_id.nil?
91
161
  "#{users_url(user_id)}/impersonation"
92
162
  end
93
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
+
94
170
  private
95
171
 
96
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.2"
2
+ VERSION = "0.7.7"
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.2
4
+ version: 0.7.7
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-06-17 00:00:00.000000000 Z
11
+ date: 2020-07-10 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