authrocket 2.4.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|