punk 0.1.4 → 0.2.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/.editorconfig +9 -0
- data/.github/workflows/test.yml +26 -1
- data/.rdoc_options +23 -0
- data/.rgignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +5 -6
- data/Gemfile.lock +16 -29
- data/README.md +1 -1
- data/VERSION +1 -1
- data/app/migrations/001_lets_punk.rb +3 -0
- data/app/routes/hello.rb +4 -0
- data/env/.gitignore +3 -0
- data/env/spec/test.sh +3 -0
- data/lib/punk/actions/.keep +0 -0
- data/lib/punk/actions/groups/list.rb +24 -0
- data/lib/punk/actions/sessions/clear.rb +21 -0
- data/lib/punk/actions/sessions/create.rb +64 -0
- data/lib/punk/actions/sessions/list.rb +18 -0
- data/lib/punk/actions/sessions/verify.rb +24 -0
- data/lib/punk/actions/tenants/list.rb +18 -0
- data/lib/punk/actions/users/list_group.rb +18 -0
- data/lib/punk/actions/users/list_tenant.rb +18 -0
- data/lib/punk/actions/users/show.rb +18 -0
- data/lib/punk/commands/list.rb +12 -6
- data/lib/punk/config/defaults.json +3 -0
- data/lib/punk/config/schema.json +3 -0
- data/lib/punk/core/app.rb +4 -6
- data/lib/punk/core/commander.rb +7 -4
- data/lib/punk/core/exec.rb +2 -0
- data/lib/punk/core/load.rb +0 -1
- data/lib/punk/framework/command.rb +5 -1
- data/lib/punk/framework/plugins/validation.rb +0 -14
- data/lib/punk/helpers/loggable.rb +1 -1
- data/lib/punk/migrations/001_punk.rb +103 -0
- data/lib/punk/models/.keep +0 -0
- data/lib/punk/models/group.rb +20 -0
- data/lib/punk/models/group_user_metadata.rb +17 -0
- data/lib/punk/models/identity.rb +29 -0
- data/lib/punk/models/session.rb +89 -0
- data/lib/punk/models/tenant.rb +19 -0
- data/lib/punk/models/tenant_user_metadata.rb +17 -0
- data/lib/punk/models/user.rb +31 -0
- data/lib/punk/routes/groups.rb +31 -0
- data/lib/punk/routes/plivo.rb +4 -0
- data/lib/punk/routes/sessions.rb +108 -0
- data/lib/punk/routes/swagger.rb +9 -0
- data/lib/punk/routes/tenants.rb +29 -0
- data/lib/punk/routes/users.rb +36 -0
- data/lib/punk/services/.keep +0 -0
- data/lib/punk/services/challenge_claim.rb +46 -0
- data/lib/punk/services/create_identities.rb +25 -0
- data/lib/punk/services/generate_swagger.rb +25 -0
- data/lib/punk/services/prove_claim.rb +29 -0
- data/lib/punk/services/secret.rb +9 -0
- data/lib/punk/templates/groups/list.jbuilder +7 -0
- data/lib/punk/templates/plivo.slim +16 -0
- data/lib/punk/templates/sessions/list.jbuilder +6 -0
- data/lib/punk/templates/sessions/pending.jbuilder +4 -0
- data/lib/punk/templates/tenants/list.jbuilder +7 -0
- data/lib/punk/templates/tenants/list.slim +8 -0
- data/lib/punk/templates/users/list.jbuilder +7 -0
- data/lib/punk/templates/users/list.rcsv +4 -0
- data/lib/punk/templates/users/show.jbuilder +5 -0
- data/lib/punk/views/groups/list.rb +22 -0
- data/lib/punk/views/plivo_store.rb +15 -0
- data/lib/punk/views/sessions/list.rb +22 -0
- data/lib/punk/views/sessions/pending.rb +28 -0
- data/lib/punk/views/tenants/list.rb +22 -0
- data/lib/punk/views/users/list.rb +22 -0
- data/lib/punk/views/users/show.rb +22 -0
- data/lib/punk/workers/.keep +0 -0
- data/lib/punk/workers/expire_sessions.rb +9 -0
- data/lib/punk/workers/geocode_session_worker.rb +48 -0
- data/lib/punk/workers/identify_session_worker.rb +45 -0
- data/lib/punk/workers/secret.rb +18 -0
- data/lib/punk/workers/send_email_worker.rb +51 -0
- data/lib/punk/workers/send_sms_worker.rb +40 -0
- data/punk.gemspec +140 -14
- data/schema.psql +345 -0
- data/spec/actions/groups/punk/list_groups_action_spec.rb +36 -0
- data/spec/actions/sessions/punk/clear_session_action_spec.rb +29 -0
- data/spec/actions/sessions/punk/create_session_action_spec.rb +33 -0
- data/spec/actions/sessions/punk/list_sessions_action_spec.rb +26 -0
- data/spec/actions/sessions/punk/verify_session_action_spec.rb +59 -0
- data/spec/actions/tenants/punk/list_tenants_action_spec.rb +25 -0
- data/spec/actions/users/punk/list_group_users_action_spec.rb +26 -0
- data/spec/actions/users/punk/list_tenant_users_action_spec.rb +26 -0
- data/spec/factories/group.rb +12 -0
- data/spec/factories/group_user_metadata.rb +10 -0
- data/spec/factories/identity.rb +19 -0
- data/spec/factories/session.rb +12 -0
- data/spec/factories/tenant.rb +10 -0
- data/spec/factories/tenant_user_metadata.rb +10 -0
- data/spec/factories/user.rb +12 -0
- data/spec/lib/commands/auth_spec.rb +11 -0
- data/spec/lib/commands/generate_spec.rb +7 -0
- data/spec/lib/commands/http_spec.rb +23 -0
- data/spec/lib/commands/list_spec.rb +7 -0
- data/spec/lib/commands/swagger_spec.rb +7 -0
- data/spec/lib/engine/punk_env_spec.rb +13 -0
- data/spec/lib/engine/punk_exec_spec.rb +9 -0
- data/spec/lib/engine/punk_init_spec.rb +9 -0
- data/spec/lib/engine/punk_store_spec.rb +10 -0
- data/spec/lib/punk.env +7 -0
- data/spec/models/punk/group_spec.rb +50 -0
- data/spec/models/punk/group_user_metadata_spec.rb +61 -0
- data/spec/models/punk/identity_spec.rb +61 -0
- data/spec/models/punk/session_spec.rb +156 -0
- data/spec/models/punk/tenant_spec.rb +51 -0
- data/spec/models/punk/tenant_user_metadata_spec.rb +61 -0
- data/spec/models/punk/user_spec.rb +115 -0
- data/spec/routes/groups/get_groups_spec.rb +33 -0
- data/spec/routes/plivo/get_plivo_spec.rb +11 -0
- data/spec/routes/sessions/delete_session_spec.rb +11 -0
- data/spec/routes/sessions/get_sessions_spec.rb +30 -0
- data/spec/routes/sessions/patch_session_spec.rb +11 -0
- data/spec/routes/sessions/post_session_spec.rb +11 -0
- data/spec/routes/swagger/get_swagger_spec.rb +12 -0
- data/spec/routes/tenants/get_tenants_spec.rb +31 -0
- data/spec/routes/users/get_users_spec.rb +60 -0
- data/spec/services/punk/challenge_claim_service_spec.rb +7 -0
- data/spec/services/punk/create_identities_service_spec.rb +14 -0
- data/spec/services/punk/generate_swagger_service_spec.rb +7 -0
- data/spec/services/punk/prove_claim_service_spec.rb +7 -0
- data/spec/services/punk/secret_service_spec.rb +7 -0
- data/spec/spec_helper.rb +122 -0
- data/spec/vcr_cassettes/PUNK_GeocodeSessionWorker/updates_the_session_data.yml +57 -0
- data/spec/vcr_cassettes/PUNK_IdentifySessionWorker/updates_the_session_data.yml +112 -0
- data/spec/views/punk/plivo_store_spec.rb +7 -0
- data/spec/views/sessions/punk/list_sessions_view_spec.rb +7 -0
- data/spec/views/sessions/punk/pending_session_view_spec.rb +7 -0
- data/spec/views/tenants/punk/list_tenants_view_spec.rb +7 -0
- data/spec/views/users/punk/list_groups_view_spec.rb +7 -0
- data/spec/views/users/punk/list_users_view_spec.rb +7 -0
- data/spec/workers/punk/expire_sessions_worker_spec.rb +31 -0
- data/spec/workers/punk/geocode_session_worker_spec.rb +14 -0
- data/spec/workers/punk/identify_session_worker_spec.rb +15 -0
- data/spec/workers/punk/secret_worker_spec.rb +20 -0
- data/spec/workers/punk/send_email_worker_spec.rb +46 -0
- data/spec/workers/punk/send_sms_worker_spec.rb +33 -0
- metadata +148 -11
- data/lib/punk/views/all.rb +0 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PUNK
|
|
4
|
+
# @model
|
|
5
|
+
# @property slug(required) [string] a unique identifier for the session while it is being challenged
|
|
6
|
+
# @property message(required) [string] a message to be displayed to the user to let them know what to do
|
|
7
|
+
class Session < PUNK::Model
|
|
8
|
+
alias to_s state
|
|
9
|
+
|
|
10
|
+
many_to_one :identity
|
|
11
|
+
one_through_one :user, join_table: :identities, left_key: :id, left_primary_key: :identity_id
|
|
12
|
+
|
|
13
|
+
symbolize :state
|
|
14
|
+
|
|
15
|
+
aasm :state do
|
|
16
|
+
state :created, initial: true
|
|
17
|
+
state :pending
|
|
18
|
+
state :active
|
|
19
|
+
state :expired
|
|
20
|
+
state :deleted
|
|
21
|
+
|
|
22
|
+
event :challenge do
|
|
23
|
+
transitions from: :created, to: :pending, guard: :current?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
event :verify, after: :erase do
|
|
27
|
+
transitions from: :pending, to: :active, guard: :current?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
event :timeout, after: :erase do
|
|
31
|
+
transitions from: [:created, :pending, :active], to: :expired
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
event :clear, after: :erase do
|
|
35
|
+
transitions from: :active, to: :deleted
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
dataset_module do
|
|
40
|
+
def created
|
|
41
|
+
where(state: 'created')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def pending
|
|
45
|
+
where(state: 'pending')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def active
|
|
49
|
+
where(state: 'active')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def expired
|
|
53
|
+
where(state: 'expired')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def deleted
|
|
57
|
+
where(state: 'deleted')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def expiring
|
|
61
|
+
where { Sequel.&({ state: ['created', 'pending'] }, (created_at < 5.minutes.ago)) }.or { Sequel.&({ state: 'active' }, ((updated_at < 1.month.ago) | (created_at < 1.year.ago))) }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validate
|
|
66
|
+
validates_presence :identity
|
|
67
|
+
validates_includes [:created, :pending, :active, :expired, :deleted], :state
|
|
68
|
+
validates_integer :attempt_count
|
|
69
|
+
validates_includes [0, 1, 2, 3], :attempt_count
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def current?
|
|
73
|
+
!timeout?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def timeout?
|
|
77
|
+
timeout! if (created? || pending?) && created_at < 5.minutes.ago || active? && (updated_at < 1.month.ago || created_at < 1.year.ago)
|
|
78
|
+
expired?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def erase
|
|
82
|
+
update(slug: nil, salt: nil, hash: nil)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def increment_attempts
|
|
86
|
+
update(attempt_count: attempt_count + 1)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PUNK
|
|
4
|
+
# @model
|
|
5
|
+
# @property id(required) [string] a unique identifier for the tenant
|
|
6
|
+
# @property name(required) [string] the name of the tenant
|
|
7
|
+
# @property icon(required) [string] an image URL
|
|
8
|
+
class Tenant < PUNK::Model
|
|
9
|
+
alias to_s name
|
|
10
|
+
|
|
11
|
+
many_to_many :users
|
|
12
|
+
one_to_many :groups
|
|
13
|
+
|
|
14
|
+
def validate
|
|
15
|
+
validates_presence :name
|
|
16
|
+
validates_url :icon, allow_blank: true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PUNK
|
|
4
|
+
class TenantUserMetadata < PUNK::Model(:tenants_users)
|
|
5
|
+
many_to_one :tenant
|
|
6
|
+
many_to_one :user
|
|
7
|
+
|
|
8
|
+
def validate
|
|
9
|
+
validates_presence :tenant
|
|
10
|
+
validates_presence :user
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
"#{tenant_id}|#{user_id}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PUNK
|
|
4
|
+
# @model
|
|
5
|
+
# @property id(required) [string] a unique identifier for the user
|
|
6
|
+
# @property name(required) [string] the name of the user
|
|
7
|
+
# @property icon(required) [string] an image URL
|
|
8
|
+
class User < Model
|
|
9
|
+
alias to_s name
|
|
10
|
+
|
|
11
|
+
many_to_many :tenants
|
|
12
|
+
many_to_many :groups
|
|
13
|
+
one_to_many :identities
|
|
14
|
+
many_through_many :sessions, through: [[:identities, :user_id, :id], [:sessions, :identity_id, :id]]
|
|
15
|
+
|
|
16
|
+
def validate
|
|
17
|
+
validates_presence :name
|
|
18
|
+
validates_url :icon, allow_blank: true
|
|
19
|
+
validates_presence :email if phone.blank?
|
|
20
|
+
validates_presence :phone if email.blank?
|
|
21
|
+
validates_email :email, allow_blank: true
|
|
22
|
+
validates_phone :phone, allow_blank: true
|
|
23
|
+
validates_unique :email, allow_blank: true
|
|
24
|
+
validates_unique :phone, allow_blank: true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def active_sessions
|
|
28
|
+
sessions_dataset.where(Sequel.lit('"sessions"."state"') => 'active')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# @resource Groups
|
|
4
|
+
#
|
|
5
|
+
# Each tenant can have many groups.
|
|
6
|
+
PUNK.route('groups') do
|
|
7
|
+
require_session!
|
|
8
|
+
require_tenant!
|
|
9
|
+
|
|
10
|
+
# Retrieve the list of groups visible to the authenticated user for a specific tenant.
|
|
11
|
+
# @path [GET] /groups
|
|
12
|
+
# @parameter tenant_id(required) [string] An email address or mobile phone number.
|
|
13
|
+
# @response [Array<Group>] 200 List of groups
|
|
14
|
+
# @method get
|
|
15
|
+
# @example 200
|
|
16
|
+
# [{
|
|
17
|
+
# "id": "deadbeef-1234-5678-abcd-000000000000",
|
|
18
|
+
# "name": "Cool Group",
|
|
19
|
+
# "icon": "https://some.image/url"
|
|
20
|
+
# }]
|
|
21
|
+
# @example 401
|
|
22
|
+
# {
|
|
23
|
+
# "message": "You must specify a tenant.",
|
|
24
|
+
# "errors": ["Cannot find tenant"]
|
|
25
|
+
# }
|
|
26
|
+
#
|
|
27
|
+
# route: GET /groups
|
|
28
|
+
get do
|
|
29
|
+
perform PUNK::ListGroupsAction, user: current_user, tenant: current_tenant
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# @resource Sessions
|
|
4
|
+
#
|
|
5
|
+
# Handle the authentication flow.
|
|
6
|
+
PUNK.route('sessions') do
|
|
7
|
+
# Challenge the claim of someone having a particular email address or phone number.
|
|
8
|
+
# @path [POST] /sessions
|
|
9
|
+
# @parameter claim(required) [string] An email address or mobile phone number.
|
|
10
|
+
# @response [Session] 201 Pending session created
|
|
11
|
+
# @response [Error] 400 Invalid claim, or the user is already authenticated
|
|
12
|
+
# @method post
|
|
13
|
+
# @example 201
|
|
14
|
+
# {
|
|
15
|
+
# "slug": "deadbeef-1234-5678-abcd-000000000000",
|
|
16
|
+
# "message": "A code has been sent to your email address."
|
|
17
|
+
# }
|
|
18
|
+
# @example 400
|
|
19
|
+
# {
|
|
20
|
+
# "message": "Validation failed",
|
|
21
|
+
# "errors": ["Claim is not an email or phone."]
|
|
22
|
+
# }
|
|
23
|
+
#
|
|
24
|
+
# route: POST /sessions
|
|
25
|
+
post do
|
|
26
|
+
require_anonymous!
|
|
27
|
+
perform PUNK::CreateSessionAction, args.merge(remote_addr: request.ip || PUNK::Session.default_values[:remote_addr].to_s, user_agent: request.env['HTTP_USER_AGENT'] || PUNK::Session.default_values[:user_agent])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# route: GET /sessions/current
|
|
31
|
+
on "current" do
|
|
32
|
+
require_session!
|
|
33
|
+
get do
|
|
34
|
+
perform PUNK::ShowUserAction, user: current_user
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# route: PATCH /sessions/:slug
|
|
39
|
+
on :id do |slug|
|
|
40
|
+
require_anonymous!
|
|
41
|
+
user_session = PUNK::Session.find(slug: slug)
|
|
42
|
+
# Verify a pending session by providing proof of access to the email address or phone number.
|
|
43
|
+
# @path [PATCH] /sessions/{slug}
|
|
44
|
+
# @parameter secret(required) [string] The verification code sent by email or sms.
|
|
45
|
+
# @response [Info] 200 Session verified and cookie created
|
|
46
|
+
# @response [Error] 400 Invalid secret
|
|
47
|
+
# @method patch
|
|
48
|
+
# @example 200
|
|
49
|
+
# {
|
|
50
|
+
# "message": "You are now logged in."
|
|
51
|
+
# }
|
|
52
|
+
# @example 400
|
|
53
|
+
# {
|
|
54
|
+
# "message": "Validation failed",
|
|
55
|
+
# "errors": ["Secret does not match."]
|
|
56
|
+
# }
|
|
57
|
+
patch do
|
|
58
|
+
view = perform PUNK::VerifySessionAction, args.merge(session: user_session)
|
|
59
|
+
request.session[:session_id] = user_session.id if user_session&.active?
|
|
60
|
+
view
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Allow the current user to access their active sessions.
|
|
65
|
+
# @path [GET] /sessions
|
|
66
|
+
# @response [Array<Session>] 200 List of sessions
|
|
67
|
+
# @response [Error] 401 The user was not authenticated
|
|
68
|
+
# @method get
|
|
69
|
+
# @example 200
|
|
70
|
+
# [{
|
|
71
|
+
# "id": "deadbeef-1234-5678-abcd-000000000000",
|
|
72
|
+
# "client": {...}
|
|
73
|
+
# }]
|
|
74
|
+
# @example 401
|
|
75
|
+
# {
|
|
76
|
+
# "message": "you are not authenticated.",
|
|
77
|
+
# "errors": ["cannot find session"]
|
|
78
|
+
# }
|
|
79
|
+
#
|
|
80
|
+
# route: GET /tenants
|
|
81
|
+
get do
|
|
82
|
+
require_session!
|
|
83
|
+
perform PUNK::ListSessionsAction, user: current_user
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Allow the current user to logout.
|
|
87
|
+
# @path [DELETE] /sessions
|
|
88
|
+
# @response [Info] 200 Session destroyed and cookie cleared
|
|
89
|
+
# @response [Error] 401 The user was not authenticated
|
|
90
|
+
# @method destroy
|
|
91
|
+
# @example 200
|
|
92
|
+
# {
|
|
93
|
+
# "message": "You have logged out."
|
|
94
|
+
# }
|
|
95
|
+
# @example 401
|
|
96
|
+
# {
|
|
97
|
+
# "message": "You are not authenticated.",
|
|
98
|
+
# "errors": ["No session exists"]
|
|
99
|
+
# }
|
|
100
|
+
#
|
|
101
|
+
# route: DELETE /sessions
|
|
102
|
+
delete do
|
|
103
|
+
require_session!
|
|
104
|
+
view = perform PUNK::ClearSessionAction, session: current_session
|
|
105
|
+
clear_session if current_session&.deleted?
|
|
106
|
+
view
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# @resource Tenants
|
|
4
|
+
#
|
|
5
|
+
# All resources in the system are relative to a particular tenant application.
|
|
6
|
+
PUNK.route('tenants') do
|
|
7
|
+
require_session!
|
|
8
|
+
|
|
9
|
+
# Retrieve the list of tenants visible to the authenticated user.
|
|
10
|
+
# @path [GET] /tenants
|
|
11
|
+
# @response [Array<Tenant>] 200 List of tenants
|
|
12
|
+
# @method get
|
|
13
|
+
# @example 200
|
|
14
|
+
# [{
|
|
15
|
+
# "id": "deadbeef-1234-5678-abcd-000000000000",
|
|
16
|
+
# "name": "Cool Tenant",
|
|
17
|
+
# "icon": "https://some.image/url"
|
|
18
|
+
# }]
|
|
19
|
+
# @example 401
|
|
20
|
+
# {
|
|
21
|
+
# "message": "You are not authenticated.",
|
|
22
|
+
# "errors": ["Cannot find session"]
|
|
23
|
+
# }
|
|
24
|
+
#
|
|
25
|
+
# route: GET /tenants
|
|
26
|
+
get do
|
|
27
|
+
perform PUNK::ListTenantsAction, user: current_user
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# @resource Users
|
|
4
|
+
#
|
|
5
|
+
# Users can belong to many tenants and many groups.
|
|
6
|
+
PUNK.route('users') do
|
|
7
|
+
require_session!
|
|
8
|
+
require_tenant!
|
|
9
|
+
|
|
10
|
+
# Retrieve the list of users visible to the authenticated user for a specific tenant or group.
|
|
11
|
+
# @path [GET] /users
|
|
12
|
+
# @parameter tenant_id(required) [string] The ID of a tenant visible to the authenticated user.
|
|
13
|
+
# @parameter group_id [string] The ID of a group that belongs to the tenant.
|
|
14
|
+
# @response [Array<User>] 200 List of users
|
|
15
|
+
# @method get
|
|
16
|
+
# @example 200
|
|
17
|
+
# [{
|
|
18
|
+
# "id": "deadbeef-1234-5678-abcd-000000000000",
|
|
19
|
+
# "name": "John Smith",
|
|
20
|
+
# "icon": "https://some.image/url"
|
|
21
|
+
# }]
|
|
22
|
+
# @example 401
|
|
23
|
+
# {
|
|
24
|
+
# "message": "You must specify a tenant.",
|
|
25
|
+
# "errors": ["Cannot find tenant"]
|
|
26
|
+
# }
|
|
27
|
+
#
|
|
28
|
+
# route: GET /users
|
|
29
|
+
get do
|
|
30
|
+
if args[:group_id]
|
|
31
|
+
perform PUNK::ListGroupUsersAction, group: current_user.groups_dataset[tenant: current_tenant, id: args[:group_id]]
|
|
32
|
+
else
|
|
33
|
+
perform PUNK::ListTenantUsersAction, tenant: current_tenant
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rbnacl'
|
|
4
|
+
|
|
5
|
+
module PUNK
|
|
6
|
+
class ChallengeClaimService < Service
|
|
7
|
+
args :session
|
|
8
|
+
|
|
9
|
+
def validate
|
|
10
|
+
validates_not_null :session
|
|
11
|
+
validates_not_empty :session
|
|
12
|
+
return if session.blank?
|
|
13
|
+
validates_type Session, :session
|
|
14
|
+
validates_state :session, :created
|
|
15
|
+
validates_event :session, :challenge
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def process
|
|
19
|
+
secret = SecretService.run.result
|
|
20
|
+
salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::SCrypt::SALTBYTES)
|
|
21
|
+
hash = RbNaCl::PasswordHash.scrypt(secret, salt, 1_048_576, 16_777_216)
|
|
22
|
+
session.update(salt: salt, hash: hash)
|
|
23
|
+
session.challenge!
|
|
24
|
+
identity = session.identity
|
|
25
|
+
case identity.claim_type
|
|
26
|
+
when :email
|
|
27
|
+
SendEmailWorker.perform_async(
|
|
28
|
+
from: 'GroupFire Accounts <noreply@groupfire.com>',
|
|
29
|
+
to: identity.claim,
|
|
30
|
+
subject: '[GroupFire] Verification Code',
|
|
31
|
+
template: 'verify',
|
|
32
|
+
tags: [:auth],
|
|
33
|
+
variables: {
|
|
34
|
+
name: identity.user&.name || 'New User',
|
|
35
|
+
secret: secret
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
when :phone
|
|
39
|
+
SendSmsWorker.perform_async(
|
|
40
|
+
to: identity.claim,
|
|
41
|
+
body: "Your GroupFire verification code is: #{secret}."
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PUNK
|
|
4
|
+
class CreateIdentitiesService < Service
|
|
5
|
+
def process
|
|
6
|
+
User.each do |user|
|
|
7
|
+
if user.email.present?
|
|
8
|
+
Identity.find_or_create(claim: user.email) do |i|
|
|
9
|
+
i.claim_type = :email
|
|
10
|
+
i.user = user
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
if user.phone.present?
|
|
14
|
+
Identity.find_or_create(claim: user.phone) do |i|
|
|
15
|
+
i.claim_type = :phone
|
|
16
|
+
i.user = user
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
rescue Sequel::ValidationFailed => e
|
|
20
|
+
logger.warn e.message
|
|
21
|
+
end
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|