keycloak_oauth 0.1.7 → 0.1.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
2
  SHA256:
3
- metadata.gz: 0121cf149dea17f792d3f007cfb5b55247e8e886c9a5d53ded44db08c0b7ebaa
4
- data.tar.gz: c9a3cba0eb30bba3684be95b5270df88156d90a90b72889c3c6dc29c5b816bd7
3
+ metadata.gz: 5b84ab145e8572eebc11e31faff2080a82ec5f157fe5579c7076f32149f4b2b7
4
+ data.tar.gz: '0308c1a7e95dbd136e1cb1c659d203b355d78faecfefa4bd56d31fc222040245'
5
5
  SHA512:
6
- metadata.gz: 1db6f2a45c114e3360951aaa73a663364f94425f731c54581b8e0d713117b9cb8efa89dc8826962c58d2b578f581d6202bbb499249085d6d657c2f54cc1df061
7
- data.tar.gz: 436dad25dd2d4987adb0abed086d23bc08a50e197d036b37df913414e1fe1bc84fddabc3e56532dadbb5d916050291be7d63266c28e7bac34ee798e2d0be24f1
6
+ metadata.gz: a9c847ab0c59e5d6c4d28c51bbc5ad8ae515874cd680b312b31e264edc97748e813a319eb83f51c75ee463edaa02a9eba65ad6a444b82c59bc805b1a6eb1cfbf
7
+ data.tar.gz: fab61c415c9a09a4cc66a6e9986998dfc1db3c126205698938ef4f76c59a3571d1078f74456ab97201f7fa578b323430767fedd5687315a6233404f3610fb01d
@@ -7,8 +7,7 @@ module KeycloakOauth
7
7
  def oauth2
8
8
  authentication_service = KeycloakOauth::AuthenticationService.new(
9
9
  authentication_params: authentication_params,
10
- session: session,
11
- redirect_uri: current_uri_without_params
10
+ session: session
12
11
  )
13
12
  authentication_service.authenticate
14
13
  map_authenticatable_if_implemented(session)
@@ -19,7 +18,7 @@ module KeycloakOauth
19
18
  private
20
19
 
21
20
  def authentication_params
22
- params.permit(:code)
21
+ params.permit(:code).merge({ redirect_uri: current_uri_without_params })
23
22
  end
24
23
 
25
24
  def map_authenticatable_if_implemented(request)
@@ -1,59 +1,31 @@
1
- require 'net/http'
2
-
3
1
  module KeycloakOauth
4
- class AuthenticationError < StandardError; end
5
-
6
2
  class AuthenticationService
7
- GRANT_TYPE = 'authorization_code'.freeze
8
- CONTENT_TYPE = 'application/x-www-form-urlencoded'.freeze
9
3
  ACCESS_TOKEN_KEY = 'access_token'.freeze
10
4
  REFRESH_TOKEN_KEY = 'refresh_token'.freeze
11
5
 
12
- attr_reader :session
6
+ attr_reader :session, :authentication_params
13
7
 
14
- def initialize(authentication_params:, session:, redirect_uri:)
15
- @code = authentication_params[:code]
8
+ def initialize(authentication_params:, session:)
9
+ @authentication_params = authentication_params
16
10
  @session = session
17
- @redirect_uri = redirect_uri
18
11
  end
19
12
 
20
13
  def authenticate
21
- store_credentials(get_tokens)
14
+ post_token_service = KeycloakOauth::PostTokenService.new(
15
+ connection: KeycloakOauth.connection,
16
+ request_params: authentication_params
17
+ )
18
+ post_token_service.perform
19
+ store_credentials(post_token_service)
22
20
  end
23
21
 
24
22
  private
25
23
 
26
- attr_reader :code, :redirect_uri
27
-
28
- def get_tokens
29
- uri = URI.parse(KeycloakOauth.connection.authentication_endpoint)
30
- Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
31
- request = Net::HTTP::Post.new(uri)
32
- request.set_content_type(CONTENT_TYPE)
33
- request.set_form_data(token_request_params)
34
- http.request(request)
35
- end
36
- end
37
-
38
- def token_request_params
39
- {
40
- client_id: KeycloakOauth.connection.client_id,
41
- client_secret: KeycloakOauth.connection.client_secret,
42
- grant_type: GRANT_TYPE,
43
- code: code,
44
- redirect_uri: redirect_uri
45
- }
46
- end
47
-
48
- def store_credentials(http_response)
49
- response_hash = JSON.parse(http_response.body)
24
+ def store_credentials(post_token_service)
25
+ response_hash = post_token_service.parsed_response_body
50
26
 
51
- if http_response.code_type == Net::HTTPOK
52
- session[:access_token] = response_hash[ACCESS_TOKEN_KEY]
53
- session[:refresh_token] = response_hash[REFRESH_TOKEN_KEY]
54
- else
55
- raise KeycloakOauth::AuthenticationError.new(response_hash)
56
- end
27
+ session[:access_token] = response_hash[ACCESS_TOKEN_KEY]
28
+ session[:refresh_token] = response_hash[REFRESH_TOKEN_KEY]
57
29
  end
58
30
  end
59
31
  end
@@ -4,21 +4,47 @@ module KeycloakOauth
4
4
  class AuthorizableError < StandardError; end
5
5
 
6
6
  class AuthorizableService
7
- HTTP_SUCCESS_CODES = [Net::HTTPOK, Net::HTTPNoContent]
7
+ HTTP_SUCCESS_CODES = [Net::HTTPOK, Net::HTTPNoContent, Net::HTTPCreated]
8
8
  DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded'.freeze
9
9
  AUTHORIZATION_HEADER = 'Authorization'.freeze
10
10
 
11
+ attr_reader :http_response, :parsed_response_body
12
+
13
+ def perform
14
+ @http_response = send_request
15
+ @parsed_response_body ||= parse_response_body(http_response)
16
+ end
17
+
11
18
  private
12
19
 
13
- def parsed_response(http_response)
14
- response = http_response.body.present? ? JSON.parse(http_response.body) : http_response.body
20
+ def parse_response_body(http_response)
21
+ response_body = http_response.body.present? ? JSON.parse(http_response.body) : http_response.body
15
22
 
16
- return response if HTTP_SUCCESS_CODES.include?(http_response.code_type)
23
+ return response_body if HTTP_SUCCESS_CODES.include?(http_response.code_type)
17
24
 
18
25
  # TODO: For now, we assume that the access token is always valid.
19
26
  # We do not yet handle the case where a refresh token is passed in and
20
27
  # used if the access token has expired.
21
- raise KeycloakOauth::AuthorizableError.new(response)
28
+ raise KeycloakOauth::AuthorizableError.new(error_message_from(response_body))
29
+ end
30
+
31
+ def error_message_from(response)
32
+ # Keycloak sometimes sends back a hash containing the "errorMessage" key,
33
+ # other times it returns a hash containing the "error_description key" key
34
+ # and other times it returns an empty string. There could be more cases,
35
+ # but for the moment we are only handling these three.
36
+ case response.class.to_s
37
+ when 'Hash'
38
+ if response.has_key?('errorMessage')
39
+ return response['errorMessage']
40
+ elsif response.has_key?('error_description')
41
+ return response['error_description']
42
+ end
43
+ when 'String'
44
+ return response
45
+ else
46
+ 'Unexpected Keycloak error'
47
+ end
22
48
  end
23
49
  end
24
50
  end
@@ -6,8 +6,8 @@ module KeycloakOauth
6
6
  @session = session
7
7
  end
8
8
 
9
- def logout
10
- parsed_response(post_logout)
9
+ def send_request
10
+ post_logout
11
11
  end
12
12
 
13
13
  private
@@ -0,0 +1,40 @@
1
+ require 'net/http'
2
+
3
+ module KeycloakOauth
4
+ class PostTokenService < KeycloakOauth::AuthorizableService
5
+ DEFAULT_GRANT_TYPE = 'authorization_code'.freeze
6
+
7
+ attr_reader :request_params, :connection
8
+
9
+ def initialize(connection:, request_params:)
10
+ @connection = connection
11
+ @request_params = request_params
12
+ end
13
+
14
+ def send_request
15
+ post_token
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :code, :redirect_uri
21
+
22
+ def post_token
23
+ uri = URI.parse(connection.authentication_endpoint)
24
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
25
+ request = Net::HTTP::Post.new(uri)
26
+ request.set_content_type(DEFAULT_CONTENT_TYPE)
27
+ request.set_form_data(token_request_params)
28
+ http.request(request)
29
+ end
30
+ end
31
+
32
+ def token_request_params
33
+ {
34
+ client_id: connection.client_id,
35
+ client_secret: connection.client_secret,
36
+ grant_type: DEFAULT_GRANT_TYPE
37
+ }.merge(request_params)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ require 'net/http'
2
+
3
+ module KeycloakOauth
4
+ class DuplicationError < StandardError; end
5
+
6
+ class PostUsersService < KeycloakOauth::AuthorizableService
7
+ CONTENT_TYPE = 'application/json'.freeze
8
+
9
+ attr_reader :request_params, :connection, :user_params
10
+
11
+ def initialize(connection:, access_token:, refresh_token:, user_params:)
12
+ @connection = connection
13
+ @access_token = access_token
14
+ @refresh_token = refresh_token
15
+ @user_params = user_params
16
+ end
17
+
18
+ def send_request
19
+ post_users
20
+ end
21
+
22
+ private
23
+
24
+ attr_accessor :access_token, :refresh_token
25
+
26
+ def post_users
27
+ uri = URI.parse(connection.post_users_endpoint)
28
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
29
+ request = Net::HTTP::Post.new(uri)
30
+ request.set_content_type(CONTENT_TYPE)
31
+ request[AUTHORIZATION_HEADER] = "Bearer #{access_token}"
32
+ request.body = user_params.to_json
33
+ http.request(request)
34
+ end
35
+ end
36
+
37
+ def parse_response_body(http_response)
38
+ super
39
+ rescue KeycloakOauth::AuthorizableError => exception
40
+ raise exception unless is_exception_a_duplication?(exception)
41
+ raise KeycloakOauth::DuplicationError.new(exception)
42
+ end
43
+
44
+ def is_exception_a_duplication?(exception)
45
+ exception.message == "User exists with same email" ||
46
+ exception.message == "User exists with same username"
47
+ end
48
+ end
49
+ end
@@ -9,8 +9,8 @@ module KeycloakOauth
9
9
  @refresh_token = refresh_token
10
10
  end
11
11
 
12
- def retrieve
13
- @user_information = parsed_response(get_user)
12
+ def send_request
13
+ get_user
14
14
  end
15
15
 
16
16
  private
@@ -19,13 +19,13 @@ module KeycloakOauth
19
19
  access_token: access_token,
20
20
  refresh_token: refresh_token
21
21
  )
22
- service.retrieve
23
- service.user_information
22
+ service.perform
23
+ service.parsed_response_body
24
24
  end
25
25
 
26
26
  def logout(session:)
27
27
  service = KeycloakOauth::LogoutService.new(session)
28
- service.logout
28
+ service.perform
29
29
  end
30
30
  end
31
31
  end
@@ -20,5 +20,9 @@ module KeycloakOauth
20
20
  def logout_endpoint
21
21
  "#{auth_url}/realms/#{realm}/protocol/openid-connect/logout"
22
22
  end
23
+
24
+ def post_users_endpoint
25
+ "#{auth_url}/admin/realms/#{realm}/users"
26
+ end
23
27
  end
24
28
  end
@@ -1,3 +1,3 @@
1
1
  module KeycloakOauth
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keycloak_oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - simplificator
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-04 00:00:00.000000000 Z
11
+ date: 2020-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -58,7 +58,7 @@ dependencies:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0'
61
- description:
61
+ description:
62
62
  email:
63
63
  - dev@simplificator.com
64
64
  executables: []
@@ -71,6 +71,8 @@ files:
71
71
  - app/services/keycloak_oauth/authentication_service.rb
72
72
  - app/services/keycloak_oauth/authorizable_service.rb
73
73
  - app/services/keycloak_oauth/logout_service.rb
74
+ - app/services/keycloak_oauth/post_token_service.rb
75
+ - app/services/keycloak_oauth/post_users_service.rb
74
76
  - app/services/keycloak_oauth/user_info_retrieval_service.rb
75
77
  - config/routes.rb
76
78
  - lib/keycloak_oauth.rb
@@ -86,7 +88,7 @@ metadata:
86
88
  homepage_uri: https://rubygems.org/gems/keycloak_oauth
87
89
  source_code_uri: https://github.com/simplificator/keycloak_oauth
88
90
  changelog_uri: https://github.com/simplificator/keycloak_oauth
89
- post_install_message:
91
+ post_install_message:
90
92
  rdoc_options: []
91
93
  require_paths:
92
94
  - lib
@@ -101,8 +103,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
103
  - !ruby/object:Gem::Version
102
104
  version: '0'
103
105
  requirements: []
104
- rubygems_version: 3.0.8
105
- signing_key:
106
+ rubygems_version: 3.1.4
107
+ signing_key:
106
108
  specification_version: 4
107
109
  summary: Implementing OAuth with Keycloak in Ruby
108
110
  test_files: []