authrocket 2.4.1 → 3.0.0
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 +4 -4
- data/CHANGELOG.md +18 -2
- data/README.md +72 -62
- data/app/controllers/auth_rocket/ar_controller.rb +11 -33
- data/app/controllers/logins_controller.rb +1 -8
- data/authrocket.gemspec +4 -3
- data/config/routes.rb +0 -1
- data/lib/authrocket.rb +22 -1
- data/lib/authrocket/api/api_config.rb +17 -18
- data/lib/authrocket/api/client.rb +1 -1
- data/lib/authrocket/api/version.rb +1 -1
- data/lib/authrocket/auth_provider.rb +49 -50
- data/lib/authrocket/client_app.rb +14 -0
- data/lib/authrocket/connection.rb +12 -0
- data/lib/authrocket/credential.rb +12 -6
- data/lib/authrocket/domain.rb +19 -0
- data/lib/authrocket/event.rb +2 -3
- data/lib/authrocket/hook.rb +39 -0
- data/lib/authrocket/invitation.rb +35 -0
- data/lib/authrocket/membership.rb +1 -1
- data/lib/authrocket/named_permission.rb +10 -0
- data/lib/authrocket/notification.rb +1 -1
- data/lib/authrocket/oauth2_session.rb +26 -0
- data/lib/authrocket/org.rb +2 -1
- data/lib/authrocket/rails/controller_helper.rb +73 -21
- data/lib/authrocket/rails/engine.rb +5 -1
- data/lib/authrocket/realm.rb +25 -9
- data/lib/authrocket/resource_link.rb +10 -0
- data/lib/authrocket/session.rb +139 -32
- data/lib/authrocket/token.rb +9 -0
- data/lib/authrocket/user.rb +88 -54
- metadata +33 -19
- data/lib/authrocket/app_hook.rb +0 -28
- data/lib/authrocket/login_policy.rb +0 -14
- data/lib/authrocket/user_token.rb +0 -9
@@ -5,68 +5,67 @@ module AuthRocket
|
|
5
5
|
belongs_to :realm
|
6
6
|
|
7
7
|
attr :name, :provider_type, :state
|
8
|
-
attr :
|
9
|
-
attr :min_complexity, :min_length, :required_chars
|
8
|
+
attr :min_complexity, :min_length
|
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
|
-
|
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,
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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,12 @@
|
|
1
|
+
module AuthRocket
|
2
|
+
class Connection < Resource
|
3
|
+
crud :all, :find, :create, :update, :delete
|
4
|
+
|
5
|
+
belongs_to :realm
|
6
|
+
|
7
|
+
attr :connection_type
|
8
|
+
attr :email_from, :email_from_name, :state
|
9
|
+
attr :smtp_host, :smtp_password, :smtp_port, :smtp_user
|
10
|
+
|
11
|
+
end
|
12
|
+
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 :
|
10
|
-
attr :
|
11
|
-
attr :
|
12
|
-
|
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).
|
18
|
-
parsed, _ = request(:post,
|
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
|
data/lib/authrocket/event.rb
CHANGED
@@ -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 :
|
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,39 @@
|
|
1
|
+
module AuthRocket
|
2
|
+
class Hook < Resource
|
3
|
+
crud :all, :find, :create, :update, :delete
|
4
|
+
|
5
|
+
belongs_to :realm
|
6
|
+
|
7
|
+
attr :accumulate, :delay, :event_type, :hook_type, :state
|
8
|
+
attr :destination
|
9
|
+
attr :email_renderer, :email_subject, :email_template, :email_to
|
10
|
+
|
11
|
+
|
12
|
+
def self.event_types
|
13
|
+
%w( invitation.org.created invitation.org.updated invitation.org.invited invitation.org.accepted invitation.org.expired
|
14
|
+
invitation.referral.created invitation.referral.updated invitation.referral.invited invitation.referral.accepted invitation.referral.expired
|
15
|
+
invitation.request.created invitation.request.updated invitation.request.invited invitation.request.accepted invitation.request.expired
|
16
|
+
membership.created membership.updated membership.deleted
|
17
|
+
org.created org.updated org.closed
|
18
|
+
user.created user.updated user.deleted
|
19
|
+
user.email.verifying user.email.verified
|
20
|
+
user.login.succeeded user.login.failed user.login.initiated
|
21
|
+
user.password.resetting user.password.updated
|
22
|
+
user.profile.updated
|
23
|
+
).sort
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.email_event_types
|
27
|
+
%w( invitation.org.invited invitation.org.accepted
|
28
|
+
invitation.referral.invited
|
29
|
+
invitation.request.invited
|
30
|
+
user.created
|
31
|
+
user.email.verifying user.email.verified
|
32
|
+
user.login.succeeded user.login.failed
|
33
|
+
user.password.resetting user.password.updated
|
34
|
+
user.profile.updated
|
35
|
+
).sort
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
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, :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
|
@@ -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
|
data/lib/authrocket/org.rb
CHANGED
@@ -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)
|
15
|
+
memberships.map(&:user)
|
15
16
|
end
|
16
17
|
|
17
18
|
def find_user(uid)
|
@@ -1,20 +1,18 @@
|
|
1
1
|
module AuthRocket::ControllerHelper
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
14
|
-
def require_valid_token
|
13
|
+
def require_login
|
15
14
|
unless current_session
|
16
|
-
|
17
|
-
redirect_to ar_login_url + "?redir=#{ERB::Util.url_encode(session[:last_url])}"
|
15
|
+
redirect_to ar_login_url(redirect_uri: safe_this_uri)
|
18
16
|
end
|
19
17
|
end
|
20
18
|
|
@@ -24,24 +22,78 @@ module AuthRocket::ControllerHelper
|
|
24
22
|
end
|
25
23
|
|
26
24
|
def current_user
|
27
|
-
current_session
|
25
|
+
current_session&.user
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_membership
|
29
|
+
# LR always sends a JWT with exactly one membership/org
|
30
|
+
# other API generated JWTs may vary
|
31
|
+
return unless current_user
|
32
|
+
current_user.memberships.each{|m| return m if m.selected }.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def current_org
|
36
|
+
current_membership&.org
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def ar_account_url(**params)
|
41
|
+
if id = params.delete(:id) || current_org&.id
|
42
|
+
loginrocket_url(path: "/accounts/#{id}", **params)
|
43
|
+
else
|
44
|
+
ar_accounts_url(**params)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# force - if false/nil, does not add ?force; else does add it
|
49
|
+
def ar_accounts_url(**params)
|
50
|
+
if params[:force] || !params.key?(:force)
|
51
|
+
params[:force] = nil
|
52
|
+
else
|
53
|
+
params.delete(:force)
|
54
|
+
end
|
55
|
+
loginrocket_url(path: '/accounts', **params)
|
56
|
+
end
|
57
|
+
|
58
|
+
def ar_login_url(**params)
|
59
|
+
loginrocket_url(path: '/login', **params)
|
28
60
|
end
|
29
61
|
|
62
|
+
def ar_logout_url(**params)
|
63
|
+
params[:session] = current_session.id if current_session
|
64
|
+
loginrocket_url(path: '/logout', **params)
|
65
|
+
end
|
30
66
|
|
31
|
-
def
|
32
|
-
|
67
|
+
def ar_profile_url(**params)
|
68
|
+
loginrocket_url(path: '/profile', **params)
|
33
69
|
end
|
34
70
|
|
35
|
-
def ar_signup_url
|
36
|
-
|
71
|
+
def ar_signup_url(**params)
|
72
|
+
loginrocket_url(path: '/signup', **params)
|
73
|
+
end
|
74
|
+
|
75
|
+
def loginrocket_url(path: nil, **params)
|
76
|
+
raise "Missing env LOGINROCKET_URL or credentials[:loginrocket_url]" if AuthRocket::Api.credentials[:loginrocket_url].blank?
|
77
|
+
uri = Addressable::URI.parse AuthRocket::Api.credentials[:loginrocket_url]
|
78
|
+
uri.path = path if path
|
79
|
+
uri.path = '/' if uri.path.blank?
|
80
|
+
uri.query_values = (uri.query_values||{}).merge(params).stringify_keys if params.present?
|
81
|
+
uri.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# returns: bool -- whether session was updated/replaced
|
86
|
+
def conditional_login
|
87
|
+
return unless params[:token]
|
88
|
+
if s = AuthRocket::Session.from_token(params[:token])
|
89
|
+
@_current_session = s
|
90
|
+
session[:ar_token] = params[:token]
|
91
|
+
true
|
92
|
+
end
|
37
93
|
end
|
38
94
|
|
39
|
-
def
|
40
|
-
|
41
|
-
s = AuthRocket::Api.credentials[:loginrocket_url].dup
|
42
|
-
s.concat('/') unless s.ends_with?('/')
|
43
|
-
s.concat(path) if path
|
44
|
-
s.freeze
|
95
|
+
def safe_this_uri
|
96
|
+
full_url_for(request.get? ? params.to_unsafe_h.except(:account, :session, :token) : {})
|
45
97
|
end
|
46
98
|
|
47
99
|
end
|