punk 0.1.2 → 0.3.2

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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/workflows/ship.yml +3 -1
  4. data/.github/workflows/test.yml +26 -1
  5. data/.rdoc_options +23 -0
  6. data/.rgignore +1 -0
  7. data/.rspec +2 -0
  8. data/Gemfile +7 -8
  9. data/Gemfile.lock +28 -41
  10. data/README.md +7 -1
  11. data/Rakefile +0 -1
  12. data/VERSION +1 -1
  13. data/app/migrations/001_lets_punk.rb +3 -0
  14. data/app/routes/hello.rb +4 -0
  15. data/env/.gitignore +3 -0
  16. data/env/spec/test.sh +3 -0
  17. data/lib/punk/actions/.keep +0 -0
  18. data/lib/punk/actions/groups/list.rb +24 -0
  19. data/lib/punk/actions/sessions/clear.rb +21 -0
  20. data/lib/punk/actions/sessions/create.rb +64 -0
  21. data/lib/punk/actions/sessions/list.rb +18 -0
  22. data/lib/punk/actions/sessions/verify.rb +24 -0
  23. data/lib/punk/actions/tenants/list.rb +18 -0
  24. data/lib/punk/actions/users/list_group.rb +18 -0
  25. data/lib/punk/actions/users/list_tenant.rb +18 -0
  26. data/lib/punk/actions/users/show.rb +18 -0
  27. data/lib/punk/commands/list.rb +12 -6
  28. data/lib/punk/config/defaults.json +3 -0
  29. data/lib/punk/config/schema.json +3 -0
  30. data/lib/punk/core/app.rb +4 -6
  31. data/lib/punk/core/commander.rb +12 -7
  32. data/lib/punk/core/exec.rb +2 -0
  33. data/lib/punk/core/load.rb +0 -1
  34. data/lib/punk/framework/command.rb +5 -1
  35. data/lib/punk/framework/plugins/validation.rb +0 -14
  36. data/lib/punk/helpers/loggable.rb +1 -1
  37. data/lib/punk/migrations/001_punk.rb +103 -0
  38. data/lib/punk/models/.keep +0 -0
  39. data/lib/punk/models/group.rb +20 -0
  40. data/lib/punk/models/group_user_metadata.rb +17 -0
  41. data/lib/punk/models/identity.rb +29 -0
  42. data/lib/punk/models/session.rb +89 -0
  43. data/lib/punk/models/tenant.rb +19 -0
  44. data/lib/punk/models/tenant_user_metadata.rb +17 -0
  45. data/lib/punk/models/user.rb +31 -0
  46. data/lib/punk/routes/groups.rb +31 -0
  47. data/lib/punk/routes/plivo.rb +4 -0
  48. data/lib/punk/routes/sessions.rb +108 -0
  49. data/lib/punk/routes/swagger.rb +9 -0
  50. data/lib/punk/routes/tenants.rb +29 -0
  51. data/lib/punk/routes/users.rb +36 -0
  52. data/lib/punk/services/.keep +0 -0
  53. data/lib/punk/services/challenge_claim.rb +46 -0
  54. data/lib/punk/services/create_identities.rb +25 -0
  55. data/lib/punk/services/generate_swagger.rb +25 -0
  56. data/lib/punk/services/prove_claim.rb +29 -0
  57. data/lib/punk/services/secret.rb +9 -0
  58. data/lib/punk/templates/groups/list.jbuilder +7 -0
  59. data/lib/punk/templates/plivo.slim +16 -0
  60. data/lib/punk/templates/sessions/list.jbuilder +6 -0
  61. data/lib/punk/templates/sessions/pending.jbuilder +4 -0
  62. data/lib/punk/templates/tenants/list.jbuilder +7 -0
  63. data/lib/punk/templates/tenants/list.slim +8 -0
  64. data/lib/punk/templates/users/list.jbuilder +7 -0
  65. data/lib/punk/templates/users/list.rcsv +4 -0
  66. data/lib/punk/templates/users/show.jbuilder +5 -0
  67. data/lib/punk/views/groups/list.rb +22 -0
  68. data/lib/punk/views/plivo_store.rb +15 -0
  69. data/lib/punk/views/sessions/list.rb +22 -0
  70. data/lib/punk/views/sessions/pending.rb +28 -0
  71. data/lib/punk/views/tenants/list.rb +22 -0
  72. data/lib/punk/views/users/list.rb +22 -0
  73. data/lib/punk/views/users/show.rb +22 -0
  74. data/lib/punk/workers/.keep +0 -0
  75. data/lib/punk/workers/expire_sessions.rb +9 -0
  76. data/lib/punk/workers/geocode_session_worker.rb +48 -0
  77. data/lib/punk/workers/identify_session_worker.rb +45 -0
  78. data/lib/punk/workers/secret.rb +18 -0
  79. data/lib/punk/workers/send_email_worker.rb +51 -0
  80. data/lib/punk/workers/send_sms_worker.rb +40 -0
  81. data/punk.gemspec +147 -21
  82. data/schema.psql +345 -0
  83. data/spec/actions/groups/punk/list_groups_action_spec.rb +36 -0
  84. data/spec/actions/sessions/punk/clear_session_action_spec.rb +29 -0
  85. data/spec/actions/sessions/punk/create_session_action_spec.rb +33 -0
  86. data/spec/actions/sessions/punk/list_sessions_action_spec.rb +26 -0
  87. data/spec/actions/sessions/punk/verify_session_action_spec.rb +59 -0
  88. data/spec/actions/tenants/punk/list_tenants_action_spec.rb +25 -0
  89. data/spec/actions/users/punk/list_group_users_action_spec.rb +26 -0
  90. data/spec/actions/users/punk/list_tenant_users_action_spec.rb +26 -0
  91. data/spec/factories/group.rb +12 -0
  92. data/spec/factories/group_user_metadata.rb +10 -0
  93. data/spec/factories/identity.rb +19 -0
  94. data/spec/factories/session.rb +12 -0
  95. data/spec/factories/tenant.rb +10 -0
  96. data/spec/factories/tenant_user_metadata.rb +10 -0
  97. data/spec/factories/user.rb +12 -0
  98. data/spec/lib/commands/auth_spec.rb +11 -0
  99. data/spec/lib/commands/generate_spec.rb +7 -0
  100. data/spec/lib/commands/http_spec.rb +23 -0
  101. data/spec/lib/commands/list_spec.rb +7 -0
  102. data/spec/lib/commands/swagger_spec.rb +7 -0
  103. data/spec/lib/engine/punk_env_spec.rb +13 -0
  104. data/spec/lib/engine/punk_exec_spec.rb +9 -0
  105. data/spec/lib/engine/punk_init_spec.rb +9 -0
  106. data/spec/lib/engine/punk_store_spec.rb +10 -0
  107. data/spec/lib/punk.env +7 -0
  108. data/spec/models/punk/group_spec.rb +50 -0
  109. data/spec/models/punk/group_user_metadata_spec.rb +61 -0
  110. data/spec/models/punk/identity_spec.rb +61 -0
  111. data/spec/models/punk/session_spec.rb +156 -0
  112. data/spec/models/punk/tenant_spec.rb +51 -0
  113. data/spec/models/punk/tenant_user_metadata_spec.rb +61 -0
  114. data/spec/models/punk/user_spec.rb +115 -0
  115. data/spec/routes/groups/get_groups_spec.rb +33 -0
  116. data/spec/routes/plivo/get_plivo_spec.rb +11 -0
  117. data/spec/routes/sessions/delete_session_spec.rb +11 -0
  118. data/spec/routes/sessions/get_sessions_spec.rb +30 -0
  119. data/spec/routes/sessions/patch_session_spec.rb +11 -0
  120. data/spec/routes/sessions/post_session_spec.rb +11 -0
  121. data/spec/routes/swagger/get_swagger_spec.rb +12 -0
  122. data/spec/routes/tenants/get_tenants_spec.rb +31 -0
  123. data/spec/routes/users/get_users_spec.rb +60 -0
  124. data/spec/services/punk/challenge_claim_service_spec.rb +7 -0
  125. data/spec/services/punk/create_identities_service_spec.rb +14 -0
  126. data/spec/services/punk/generate_swagger_service_spec.rb +7 -0
  127. data/spec/services/punk/prove_claim_service_spec.rb +7 -0
  128. data/spec/services/punk/secret_service_spec.rb +7 -0
  129. data/spec/spec_helper.rb +122 -0
  130. data/spec/vcr_cassettes/PUNK_GeocodeSessionWorker/updates_the_session_data.yml +57 -0
  131. data/spec/vcr_cassettes/PUNK_IdentifySessionWorker/updates_the_session_data.yml +112 -0
  132. data/spec/views/punk/plivo_store_spec.rb +7 -0
  133. data/spec/views/sessions/punk/list_sessions_view_spec.rb +7 -0
  134. data/spec/views/sessions/punk/pending_session_view_spec.rb +7 -0
  135. data/spec/views/tenants/punk/list_tenants_view_spec.rb +7 -0
  136. data/spec/views/users/punk/list_groups_view_spec.rb +7 -0
  137. data/spec/views/users/punk/list_users_view_spec.rb +7 -0
  138. data/spec/workers/punk/expire_sessions_worker_spec.rb +31 -0
  139. data/spec/workers/punk/geocode_session_worker_spec.rb +14 -0
  140. data/spec/workers/punk/identify_session_worker_spec.rb +15 -0
  141. data/spec/workers/punk/secret_worker_spec.rb +20 -0
  142. data/spec/workers/punk/send_email_worker_spec.rb +46 -0
  143. data/spec/workers/punk/send_sms_worker_spec.rb +33 -0
  144. metadata +154 -16
  145. data/lib/punk/views/all.rb +0 -4
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
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class GenerateSwaggerService < Service
5
+ def process
6
+ path = File.join(PUNK.get.app.path, '..', 'www', 'swagger.json')
7
+ raise InternalServerError, 'swagger.json already exists' if File.exist?(path) && !PUNK.env.test?
8
+ require 'swagger_yard'
9
+ require_relative '../helpers/swagger'
10
+ SwaggerYard.register_custom_yard_tags!
11
+ SwaggerYard.configure do |config|
12
+ config.api_version = PUNK.version
13
+ config.title = PUNK.get.app.name
14
+ config.description = PUNK.get.app.description
15
+ config.api_base_path = PUNK.get.app.url
16
+ config.controller_path = [File.join(PUNK.get.app.path, 'routes', '**', '*'), File.join(__dir__, '..', 'routes', '**', '*')]
17
+ config.model_path = [File.join(__dir__, '..', 'models', '**', '*'), File.join(__dir__, '..', 'views', '**', '*'), File.join(PUNK.get.app.path, 'models', '**', '*')]
18
+ end
19
+ spec = SwaggerYard::OpenAPI.new
20
+ blob = JSON.pretty_generate(spec.to_h)
21
+ File.open(path, "w") { |f| f << blob } unless PUNK.env.test?
22
+ blob
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbnacl'
4
+
5
+ module PUNK
6
+ class ProveClaimService < Service
7
+ args :session, :secret
8
+
9
+ def validate
10
+ validates_not_null :session
11
+ validates_not_empty :session
12
+ return if session.blank?
13
+ session.timeout?
14
+ validates_type Session, :session
15
+ validates_state :session, :pending
16
+ validates_event :session, :verify
17
+ end
18
+
19
+ def process
20
+ session.increment_attempts
21
+ session.reload
22
+ raise BadRequest, "Too many attempts" if session.attempt_count >= 3
23
+ hash = RbNaCl::PasswordHash.scrypt(secret, session.salt, 1_048_576, 16_777_216)
24
+ proven = (session[:hash] == hash)
25
+ session.verify! if proven
26
+ proven
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class SecretService < Service
5
+ def process
6
+ (SecureRandom.random_number(900_000) + 100_000).to_s
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.array!(groups) do |group|
4
+ json.id group.id
5
+ json.name group.name
6
+ json.icon group.icon
7
+ end
@@ -0,0 +1,16 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Plivo
5
+ body
6
+ h2 Plivo
7
+ - plivo = PUNK.cache.get(:plivo) || []
8
+ - plivo.each do |message|
9
+ h4= message[:sent]
10
+ dl
11
+ dt from
12
+ dd= message[:from]
13
+ dt to
14
+ dd= message[:to]
15
+ dt body
16
+ dd= message[:body]
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.array!(sessions) do |session|
4
+ json.id session.id
5
+ json.data session.data
6
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.slug session.slug
4
+ json.message message
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.array!(tenants) do |tenant|
4
+ json.id tenant.id
5
+ json.name tenant.name
6
+ json.icon tenant.icon
7
+ end
@@ -0,0 +1,8 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Tenants
5
+ body
6
+ - tenants.each do |tenant|
7
+ h1= tenant.name
8
+ img src= tenant.icon
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.array!(users) do |user|
4
+ json.id user.id
5
+ json.name user.name
6
+ json.icon user.icon
7
+ end
@@ -0,0 +1,4 @@
1
+ csv << ['ID','NAME','ICON','EMAIL','PHONE','TITLE','COMPANY','WEBSITE','LOCATION']
2
+ users.each do |user|
3
+ csv << [user.id, user.name, user.icon, user.email, user.phone, user.profile[:professional][:title], user.profile[:professional][:company], user.profile[:professional][:website], user.profile[:professional][:location]]
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.id user.id
4
+ json.name user.name
5
+ json.icon user.icon
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListGroupsView < View
5
+ args :groups
6
+
7
+ def validate
8
+ validates_not_null :groups
9
+ validates_type Array, :groups
10
+ end
11
+
12
+ def process
13
+ 'groups/list'
14
+ end
15
+
16
+ protected
17
+
18
+ def _dir
19
+ File.join(__dir__, '..', '..', 'templates')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class PlivoStore < View
5
+ def process
6
+ 'plivo'
7
+ end
8
+
9
+ protected
10
+
11
+ def _dir
12
+ File.join(__dir__, '..', 'templates')
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListSessionsView < View
5
+ args :sessions
6
+
7
+ def validate
8
+ validates_not_null :sessions
9
+ validates_type Array, :sessions
10
+ end
11
+
12
+ def process
13
+ 'sessions/list'
14
+ end
15
+
16
+ protected
17
+
18
+ def _dir
19
+ File.join(__dir__, '..', '..', 'templates')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class PendingSessionView < View
5
+ args :session, :message
6
+
7
+ def validate
8
+ validates_not_null :session
9
+ validates_not_empty :session
10
+ return if session.blank?
11
+ validates_type Session, :session
12
+ validates_state :session, :pending
13
+ validates_not_null :message
14
+ validates_not_empty :message
15
+ validates_type String, :message
16
+ end
17
+
18
+ def process
19
+ 'sessions/pending'
20
+ end
21
+
22
+ protected
23
+
24
+ def _dir
25
+ File.join(__dir__, '..', '..', 'templates')
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListTenantsView < View
5
+ args :tenants
6
+
7
+ def validate
8
+ validates_not_null :tenants
9
+ validates_type Array, :tenants
10
+ end
11
+
12
+ def process
13
+ 'tenants/list'
14
+ end
15
+
16
+ protected
17
+
18
+ def _dir
19
+ File.join(__dir__, '..', '..', 'templates')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListUsersView < View
5
+ args :users
6
+
7
+ def validate
8
+ validates_not_null :users
9
+ validates_type Array, :users
10
+ end
11
+
12
+ def process
13
+ 'users/list'
14
+ end
15
+
16
+ protected
17
+
18
+ def _dir
19
+ File.join(__dir__, '..', '..', 'templates')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ShowUserView < View
5
+ args :user
6
+
7
+ def validate
8
+ validates_not_null :user
9
+ validates_type User, :user
10
+ end
11
+
12
+ def process
13
+ 'users/show'
14
+ end
15
+
16
+ protected
17
+
18
+ def _dir
19
+ File.join(__dir__, '..', '..', 'templates')
20
+ end
21
+ end
22
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ExpireSessionsWorker < Worker
5
+ def process
6
+ Session.expiring.each(&:timeout?)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class GeocodeSessionWorker < Worker
5
+ args :session_id
6
+
7
+ def validate
8
+ validates_not_null :session_id
9
+ validates_not_empty :session_id
10
+ end
11
+
12
+ def process
13
+ require 'ipstack'
14
+
15
+ session = Session[session_id]
16
+ return if session.blank?
17
+
18
+ ip_address = session.remote_addr.to_s
19
+ return if ip_address == '127.0.0.1'
20
+
21
+ return if PUNK.get.ipstack.api_key.blank?
22
+ result = Ipstack::API.standard(ip_address).deep_symbolize_keys
23
+
24
+ raise if result.blank?
25
+
26
+ timezone = result[:time_zone][:code] if result[:time_zone].present?
27
+ language = result[:location][:languages].first[:code] if result[:location].present? && result[:location][:languages].present?
28
+ currency = result[:currency][:code] if result[:currency].present?
29
+ session.update(data: session.data.merge(
30
+ tz: timezone,
31
+ lang: language,
32
+ currency: currency,
33
+ geo: {
34
+ lat: result[:latitude],
35
+ lng: result[:longitude]
36
+ },
37
+ location: {
38
+ city: result[:city],
39
+ region: result[:region_name],
40
+ country: result[:country_name],
41
+ continent: result[:continent_name]
42
+ }
43
+ ))
44
+
45
+ session.save
46
+ end
47
+ end
48
+ end