authrocket 2.4.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@ module AuthRocket
5
5
  module ClassMethods
6
6
 
7
7
  def parse_credentials(creds)
8
- creds.with_indifferent_access.except :loginrocket_url, :jwt_secret
8
+ creds.with_indifferent_access.except :loginrocket_url, :jwt_key
9
9
  end
10
10
 
11
11
  end
@@ -1,3 +1,3 @@
1
1
  module AuthRocket
2
- VERSION = '2.4.1'
2
+ VERSION = '3.3.0'
3
3
  end
@@ -5,68 +5,67 @@ module AuthRocket
5
5
  belongs_to :realm
6
6
 
7
7
  attr :name, :provider_type, :state
8
- attr :email_verification, :login, :name_field, :password_field, :signup, :signup_mode, :verify
9
8
  attr :min_complexity, :min_length, :required_chars
10
9
  attr :client_id, :client_secret, :scopes
10
+ attr :loginrocket_domain
11
+ attr :authorization_url, :profile_url, :token_url
12
+ attr :email_field, :email_verified_field, :first_name_field, :id_field, :last_name_field, :name_field
13
+ attr :auth_url # readonly
11
14
 
12
15
 
13
- # attribs - :redirect_uri - required
14
- # - :nonce - optional
15
- def self.authorize_urls(attribs={})
16
- params = parse_request_params(attribs)
17
- parsed, creds = request(:get, url+'/authorize', params)
18
- if parsed[:errors].any?
19
- raise Error, parsed[:errors].inspect
20
- end
21
- NCore::Collection.new.tap do |coll|
22
- coll.metadata = parsed[:metadata]
23
- parsed[:data].each do |hash|
24
- coll << GenericObject.new(hash.merge(metadata: parsed[:metadata]), creds)
25
- end
26
- end
27
- end
28
-
29
- # attribs - :redirect_uri - required
30
- # - :nonce - optional
31
- def self.authorize_url(auth_provider_id, attribs={})
32
- params = parse_request_params(attribs)
33
- parsed, creds = request(:get, url+"/#{auth_provider_id}/authorize", params)
34
- if parsed[:errors].any?
35
- raise Error, parsed[:errors].inspect
36
- end
37
- parsed[:data][:url]
38
- end
39
-
40
16
  # same as self.authorize_url(self.id, ...)
41
17
  def authorize_url(attribs={})
42
- params = parse_request_params(attribs).merge credentials: api_creds
43
- self.class.authorize_url(id, params)
44
- end
45
-
46
- # attribs - :code - required
47
- # - :nonce - optional
48
- # - :state - required
49
- # always returns a new object; check .errors? or .valid? to see how it went
50
- def self.authorize(attribs={})
51
- params = parse_request_params(attribs)
52
- parsed, creds = request(:post, url+'/authorize', params)
53
- if parsed[:data][:object] == 'user_token'
54
- UserToken.new(parsed, creds)
55
- else
56
- User.new(parsed, creds)
57
- end
18
+ self.class.authorize_url id, attribs.reverse_merge(credentials: api_creds)
58
19
  end
59
20
 
60
21
  # attribs - :access_token - required
22
+ # returns: Session
61
23
  # always returns a new object; check .errors? or .valid? to see how it went
62
24
  def authorize_token(attribs={})
63
25
  params = parse_request_params(attribs)
64
- parsed, creds = request(:post, url+'/authorize', params)
65
- if parsed[:data][:object] == 'user_token'
66
- UserToken.new(parsed, creds)
67
- else
68
- User.new(parsed, creds)
26
+ parsed, creds = request(:post, resource_path+'/authorize', params)
27
+ self.class.factory(parsed, creds)
28
+ end
29
+
30
+
31
+ class << self
32
+
33
+ # attribs - :redirect_uri - required
34
+ # - :nonce - optional
35
+ # returns: Array of simplified AuthProviders
36
+ def authorize_urls(attribs={})
37
+ params = parse_request_params(attribs)
38
+ parsed, creds = request(:get, resource_path+'/authorize', params)
39
+ raise QueryError, parsed[:errors] if parsed[:errors].any?
40
+ NCore::Collection.new.tap do |coll|
41
+ coll.metadata = parsed[:metadata]
42
+ parsed[:data].each do |hash|
43
+ coll << factory(hash.merge(metadata: parsed[:metadata]), creds)
44
+ end
45
+ end
46
+ end
47
+
48
+ # attribs - :redirect_uri - required
49
+ # - :nonce - optional
50
+ # returns: simplified AuthProvider
51
+ def authorize_url(auth_provider_id, attribs={})
52
+ params = parse_request_params(attribs)
53
+ parsed, creds = request(:get, resource_path+"/#{CGI.escape auth_provider_id}/authorize", params)
54
+ raise QueryError, parsed[:errors] if parsed[:errors].any?
55
+ factory(parsed, creds)
69
56
  end
57
+
58
+ # attribs - :code - required
59
+ # - :nonce - optional
60
+ # - :state - required
61
+ # returns: Session
62
+ # always returns a new object; check .errors? or .valid? to see how it went
63
+ def authorize(attribs={})
64
+ params = parse_request_params(attribs)
65
+ parsed, creds = request(:post, resource_path+'/authorize', params)
66
+ factory(parsed, creds)
67
+ end
68
+
70
69
  end
71
70
 
72
71
  end
@@ -0,0 +1,14 @@
1
+ module AuthRocket
2
+ class ClientApp < Resource
3
+ crud :all, :find, :create, :update, :delete
4
+
5
+ belongs_to :realm
6
+
7
+ attr :client_type, :name, :redirect_uris
8
+ # standard:
9
+ attr :state
10
+ # oauth2:
11
+ attr :allowed_scopes, :app_type, :logo, :secret, :trusted
12
+
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module AuthRocket
2
+ class Connection < Resource
3
+ crud :all, :find, :create, :update, :delete
4
+
5
+ belongs_to :realm
6
+
7
+ attr :connection_name, :connection_type, :state
8
+ attr :email_from, :email_from_name
9
+ attr :smtp_host, :smtp_password, :smtp_port, :smtp_user
10
+ attr :api_endpoint, :provider_account, :valid_list_ids
11
+
12
+ end
13
+ end
@@ -3,19 +3,25 @@ module AuthRocket
3
3
  crud :find, :create, :update, :delete
4
4
 
5
5
  belongs_to :auth_provider
6
+ belongs_to :client_app
6
7
  belongs_to :user
7
8
 
8
9
  attr :credential_type
9
- attr :api_key
10
- attr :password, :password_confirmation
11
- attr :name, :otp_secret, :provisioning_uri, :state
12
- attr :access_token, :provider_user_id, :token_expires_at
10
+ attr :password, :password_confirmation # writeonly
11
+ attr :name, :otp_secret, :provisioning_svg, :provisioning_uri, :state
12
+ attr :access_token, :provider_user_id
13
+ attr_datetime :token_expires_at
14
+ attr :client_app_name, :approved_scopes
13
15
 
14
16
 
17
+ def provisioning_svg
18
+ self[:provisioning_svg]&.html_safe
19
+ end
20
+
15
21
  # code - required
16
22
  def verify(code, attribs={})
17
- params = parse_request_params(attribs.merge(code: code), json_root: json_root).merge credentials: api_creds
18
- parsed, _ = request(:post, url+'/verify', params)
23
+ params = parse_request_params(attribs.merge(code: code), json_root: json_root).reverse_merge credentials: api_creds
24
+ parsed, _ = request(:post, resource_path+'/verify', params)
19
25
  load(parsed)
20
26
  errors.empty? ? self : false
21
27
  end
@@ -0,0 +1,19 @@
1
+ module AuthRocket
2
+ class Domain < Resource
3
+ crud :all, :find, :create, :update, :delete
4
+
5
+ belongs_to :realm
6
+
7
+ attr :cert_state, :dns_state, :domain_type, :flags, :fqdn, :state
8
+ attr :subdomain
9
+ attr :domain
10
+
11
+ def verify(attribs={})
12
+ params = parse_request_params(attribs, json_root: json_root).reverse_merge credentials: api_creds
13
+ parsed, _ = request(:post, resource_path+'/verify', params)
14
+ load(parsed)
15
+ errors.empty? ? self : false
16
+ end
17
+
18
+ end
19
+ end
@@ -2,16 +2,15 @@ module AuthRocket
2
2
  class Event < Resource
3
3
  crud :all, :find
4
4
 
5
- belongs_to :app_hook
6
5
  belongs_to :auth_provider
7
- belongs_to :login_policy
6
+ belongs_to :invitation
8
7
  belongs_to :membership
9
8
  belongs_to :org
10
9
  belongs_to :realm
11
10
  belongs_to :user
12
11
  has_many :notifications
13
12
 
14
- attr :event_type
13
+ attr :event_type, :token
15
14
  attr_datetime :event_at
16
15
 
17
16
  def request_data
@@ -0,0 +1,42 @@
1
+ module AuthRocket
2
+ class Hook < Resource
3
+ crud :all, :find, :create, :update, :delete
4
+
5
+ belongs_to :realm
6
+ belongs_to :connection
7
+
8
+ attr :accumulate, :delay, :event_type, :hook_type, :state
9
+ attr :destination
10
+ attr :email_renderers, :email_subjects, :email_templates, :email_to, :locales
11
+ attr :current_locales # readonly
12
+ attr :description, :list_id, :name, :on_create, :visibility
13
+
14
+
15
+ def self.event_types
16
+ %w( invitation.org.created invitation.org.updated invitation.org.invited invitation.org.accepted invitation.org.expired
17
+ invitation.referral.created invitation.referral.updated invitation.referral.invited invitation.referral.accepted invitation.referral.expired
18
+ invitation.request.created invitation.request.updated invitation.request.invited invitation.request.accepted invitation.request.expired
19
+ membership.created membership.updated membership.deleted
20
+ org.created org.updated org.closed
21
+ user.created user.updated user.deleted
22
+ user.email.verifying user.email.verified
23
+ user.login.succeeded user.login.failed user.login.initiated
24
+ user.password.resetting user.password.updated
25
+ user.profile.updated
26
+ ).sort
27
+ end
28
+
29
+ def self.email_event_types
30
+ %w( invitation.org.invited invitation.org.accepted
31
+ invitation.referral.invited
32
+ invitation.request.invited
33
+ user.created
34
+ user.email.verifying user.email.verified
35
+ user.login.succeeded user.login.failed
36
+ user.password.resetting user.password.updated
37
+ user.profile.updated
38
+ ).sort
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,26 @@
1
+ module AuthRocket
2
+ class HookState < Resource
3
+ crud :all, :find, :create, :update, :delete
4
+
5
+ belongs_to :hook
6
+ belongs_to :user
7
+
8
+ attr :hook_state_type
9
+ attr :list_state
10
+
11
+
12
+ private
13
+
14
+ def create(attribs={})
15
+ if self[:user_id]
16
+ if attribs.key? json_root
17
+ attribs[json_root][:user_id] ||= self[:user_id]
18
+ else
19
+ attribs[:user_id] ||= self[:user_id]
20
+ end
21
+ end
22
+ super attribs
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ module AuthRocket
2
+ class Invitation < Resource
3
+ crud :all, :find, :create, :update, :delete
4
+
5
+ belongs_to :inviting_user, class_name: 'AuthRocket::User'
6
+ belongs_to :org
7
+ belongs_to :realm
8
+ has_many :events
9
+
10
+ attr :email, :invitation_type, :locale, :token
11
+ attr :permissions
12
+ attr_datetime :created_at, :expires_at, :invited_at
13
+
14
+ def any_permission?(*perms)
15
+ perms.any? do |p|
16
+ case p
17
+ when String
18
+ permissions.include? p
19
+ when Regexp
20
+ permissions.any?{|m| p =~ m}
21
+ else
22
+ false
23
+ end
24
+ end
25
+ end
26
+
27
+ def invite(attribs={})
28
+ params = parse_request_params(attribs, json_root: json_root).reverse_merge credentials: api_creds
29
+ parsed, _ = request(:post, resource_path+'/invite', params)
30
+ load(parsed)
31
+ errors.empty? ? self : false
32
+ end
33
+
34
+ end
35
+ end
@@ -6,7 +6,7 @@ module AuthRocket
6
6
  belongs_to :user
7
7
  has_many :events
8
8
 
9
- attr :custom, :permissions
9
+ attr :permissions, :selected
10
10
  attr_datetime :expires_at
11
11
 
12
12
 
@@ -0,0 +1,10 @@
1
+ module AuthRocket
2
+ class NamedPermission < Resource
3
+ crud :find, :create, :update, :delete
4
+
5
+ belongs_to :realm
6
+
7
+ attr :auto_grant, :name, :permission
8
+
9
+ end
10
+ end
@@ -1,8 +1,8 @@
1
1
  module AuthRocket
2
2
  class Notification < Resource
3
3
 
4
- belongs_to :app_hook
5
4
  belongs_to :event
5
+ belongs_to :hook
6
6
 
7
7
  attr :attempts, :hook_type, :last_destination, :last_result, :state
8
8
  attr_datetime :last_attempt_at
@@ -0,0 +1,26 @@
1
+ module AuthRocket
2
+ class Oauth2Session < Resource
3
+ crud :find, :create
4
+
5
+ attr :access_token, :code, :id_token, :redirect_uri
6
+ attr :profile
7
+ attr :expires_in, :token_type
8
+ attr_datetime :expires_at
9
+
10
+ def self.json_root ; 'session' ; end
11
+ def self.resource_path ; 'sessions/oauth2' ; end
12
+
13
+ class << self
14
+
15
+ # params - {client_app_id:, client_app_secret:, code:}
16
+ # returns: Token - must check .valid? or .errors? for response
17
+ def code_to_token(params={})
18
+ params = parse_request_params(params, json_root: json_root)
19
+ parsed, creds = request(:post, "#{resource_path}/code", params)
20
+ factory(parsed, creds)
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -4,6 +4,7 @@ module AuthRocket
4
4
 
5
5
  belongs_to :realm
6
6
  has_many :events
7
+ has_many :invitations
7
8
  has_many :memberships
8
9
 
9
10
  attr :custom, :name, :reference, :state
@@ -11,7 +12,7 @@ module AuthRocket
11
12
 
12
13
 
13
14
  def users
14
- memberships.map(&:user).compact
15
+ memberships.map(&:user)
15
16
  end
16
17
 
17
18
  def find_user(uid)
@@ -1,20 +1,26 @@
1
1
  module AuthRocket::ControllerHelper
2
2
  extend ActiveSupport::Concern
3
3
 
4
- included do
5
- if respond_to?(:helper_method)
6
- helper_method :current_session
7
- helper_method :current_user
8
- helper_method :ar_login_url
9
- helper_method :ar_signup_url
4
+ private
5
+
6
+ def process_inbound_token
7
+ # if GET (the only method LR uses), redirect to remove ?token=
8
+ if request.get? && conditional_login
9
+ redirect_to safe_this_uri
10
10
  end
11
11
  end
12
12
 
13
+ def process_authorization_header
14
+ if request.headers['authorization'] =~ %r{Bearer (.+)$}i
15
+ if s = AuthRocket::Session.from_token($1)
16
+ @_current_session = s
17
+ end
18
+ end
19
+ end
13
20
 
14
- def require_valid_token
21
+ def require_login
15
22
  unless current_session
16
- session[:last_url] = request.get? ? url_for(params.to_unsafe_h.except(:domain, :host, :port, :prototcol, :subdomain, :token)) : url_for
17
- redirect_to ar_login_url + "?redir=#{ERB::Util.url_encode(session[:last_url])}"
23
+ redirect_to ar_login_url(redirect_uri: safe_this_uri)
18
24
  end
19
25
  end
20
26
 
@@ -24,24 +30,78 @@ module AuthRocket::ControllerHelper
24
30
  end
25
31
 
26
32
  def current_user
27
- current_session.try(:user)
33
+ current_session&.user
34
+ end
35
+
36
+ def current_membership
37
+ # LR always sends a JWT with exactly one membership/org
38
+ # other API generated JWTs may vary
39
+ return unless current_user
40
+ current_user.memberships.each{|m| return m if m.selected }.first
41
+ end
42
+
43
+ def current_org
44
+ current_membership&.org
28
45
  end
29
46
 
30
47
 
31
- def ar_login_url
32
- @_login_url = loginrocket_url('login')
48
+ def ar_account_url(**params)
49
+ if id = params.delete(:id) || current_org&.id
50
+ loginrocket_url(path: "/accounts/#{id}", **params)
51
+ else
52
+ ar_accounts_url(**params)
53
+ end
54
+ end
55
+
56
+ # force - if false/nil, does not add ?force; else does add it
57
+ def ar_accounts_url(**params)
58
+ if params[:force] || !params.key?(:force)
59
+ params[:force] = nil
60
+ else
61
+ params.delete(:force)
62
+ end
63
+ loginrocket_url(path: '/accounts', **params)
33
64
  end
34
65
 
35
- def ar_signup_url
36
- @_signup_url = loginrocket_url('signup')
66
+ def ar_login_url(**params)
67
+ loginrocket_url(path: '/login', **params)
68
+ end
69
+
70
+ def ar_logout_url(**params)
71
+ params[:session] = current_session.id if current_session
72
+ loginrocket_url(path: '/logout', **params)
73
+ end
74
+
75
+ def ar_profile_url(**params)
76
+ loginrocket_url(path: '/profile', **params)
77
+ end
78
+
79
+ def ar_signup_url(**params)
80
+ loginrocket_url(path: '/signup', **params)
81
+ end
82
+
83
+ def loginrocket_url(path: nil, **params)
84
+ raise "Missing env LOGINROCKET_URL or credentials[:loginrocket_url]" if AuthRocket::Api.credentials[:loginrocket_url].blank?
85
+ uri = Addressable::URI.parse AuthRocket::Api.credentials[:loginrocket_url]
86
+ uri.path = path if path
87
+ uri.path = '/' if uri.path.blank?
88
+ uri.query_values = (uri.query_values||{}).merge(params).stringify_keys if params.present?
89
+ uri.to_s
90
+ end
91
+
92
+
93
+ # returns: bool -- whether session was updated/replaced
94
+ def conditional_login
95
+ return unless params[:token]
96
+ if s = AuthRocket::Session.from_token(params[:token])
97
+ @_current_session = s
98
+ session[:ar_token] = params[:token]
99
+ true
100
+ end
37
101
  end
38
102
 
39
- def loginrocket_url(path=nil)
40
- raise "Missing env AUTHROCKET_LOGIN_URL or credentials[:loginrocket_url]" if AuthRocket::Api.credentials[:loginrocket_url].blank?
41
- s = AuthRocket::Api.credentials[:loginrocket_url].dup
42
- s.concat('/') unless s.ends_with?('/')
43
- s.concat(path) if path
44
- s.freeze
103
+ def safe_this_uri
104
+ full_url_for(request.get? ? params.to_unsafe_h.except(:account, :session, :token) : {})
45
105
  end
46
106
 
47
107
  end