jobshop 0.0.131 → 0.0.157

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +16 -9
  4. data/app/controllers/concerns/jobshop/authentication_handler.rb +15 -0
  5. data/app/controllers/concerns/jobshop/authorization_handler.rb +29 -0
  6. data/app/controllers/jobshop/application_controller.rb +5 -32
  7. data/app/controllers/jobshop/organizations/lookups_controller.rb +18 -0
  8. data/app/controllers/jobshop/places_controller.rb +1 -1
  9. data/app/controllers/jobshop/{welcome/registrations_controller.rb → registrations_controller.rb} +2 -7
  10. data/app/controllers/jobshop/session_activations_controller.rb +1 -1
  11. data/app/controllers/jobshop/sessions_controller.rb +15 -1
  12. data/app/controllers/jobshop/users_controller.rb +9 -0
  13. data/app/mailers/jobshop/application_mailer.rb +2 -2
  14. data/app/models/jobshop/ability.rb +7 -0
  15. data/app/models/jobshop/collection.rb +22 -0
  16. data/app/models/jobshop/company.rb +22 -0
  17. data/app/models/jobshop/dashboard.rb +1 -1
  18. data/app/models/jobshop/inspection/boolean_criterion.rb +44 -0
  19. data/app/models/jobshop/inspection/criterion.rb +22 -0
  20. data/app/models/jobshop/inspection/deviation_criterion.rb +112 -0
  21. data/app/models/jobshop/inspection/limit_criterion.rb +114 -0
  22. data/app/models/jobshop/inspection/report.rb +41 -0
  23. data/app/models/jobshop/inspection/result.rb +42 -0
  24. data/app/models/jobshop/inspection/tuple.rb +13 -0
  25. data/app/models/jobshop/inspection.rb +13 -0
  26. data/app/models/jobshop/order.rb +41 -0
  27. data/app/models/jobshop/order_line.rb +13 -0
  28. data/app/models/jobshop/organization.rb +34 -0
  29. data/app/models/jobshop/place.rb +6 -4
  30. data/app/models/jobshop/product.rb +23 -0
  31. data/app/models/jobshop/registration.rb +14 -10
  32. data/app/models/jobshop/role.rb +24 -0
  33. data/app/models/jobshop/role_ability.rb +7 -0
  34. data/app/models/jobshop/role_assignment.rb +7 -0
  35. data/app/models/jobshop/routing_process.rb +20 -0
  36. data/app/models/jobshop/routing_step.rb +11 -0
  37. data/app/models/jobshop/thing.rb +30 -4
  38. data/app/models/jobshop/user.rb +23 -25
  39. data/app/serializers/jobshop/test_user_serializer.rb +10 -0
  40. data/app/services/jobshop/authentication_service.rb +20 -0
  41. data/app/services/jobshop/authorization_service.rb +30 -0
  42. data/app/services/jobshop/jwt_service.rb +17 -0
  43. data/config/routes.rb +2 -43
  44. data/db/migrate/20170311194758_initialize_jobshop.rb +1 -106
  45. data/db/migrate/20171216021339_create_organizations.rb +20 -0
  46. data/db/migrate/20171216021717_create_users.rb +30 -0
  47. data/db/migrate/20171216021853_create_companies.rb +25 -0
  48. data/db/migrate/20171216022020_create_places.rb +22 -0
  49. data/db/migrate/20171216022135_create_products.rb +27 -0
  50. data/db/migrate/20171216022605_create_orders.rb +53 -0
  51. data/db/migrate/20171216023018_create_roles.rb +52 -0
  52. data/db/migrate/20171216023022_create_sessions.rb +21 -0
  53. data/db/migrate/20171216035357_create_things.rb +44 -0
  54. data/db/migrate/20171219022118_create_routing_processes.rb +57 -0
  55. data/db/migrate/20180107203241_create_inspection.rb +159 -0
  56. data/{app/assets/stylesheets/jobshop/welcome.scss → db/seeds.rb} +0 -0
  57. data/lib/generators/jobshop/app/app_generator.rb +36 -3
  58. data/lib/generators/jobshop/app/templates/config/database.yml.tt +19 -0
  59. data/lib/generators/jobshop/config/templates/config/initializers/jobshop.rb.tt +0 -2
  60. data/lib/generators/jobshop/dummy/dummy_generator.rb +3 -14
  61. data/lib/jobshop/cli/spinner.rb +21 -0
  62. data/lib/jobshop/cli.rb +44 -24
  63. data/lib/jobshop/configuration.rb +24 -0
  64. data/lib/jobshop/dummy_app.rb +155 -0
  65. data/lib/jobshop/engine.rb +30 -31
  66. data/lib/jobshop/helpers/migration.rb +55 -0
  67. data/lib/jobshop/version.rb +2 -2
  68. data/lib/jobshop.rb +7 -21
  69. data/lib/tasks/jobshop_tasks.rake +7 -24
  70. metadata +113 -150
  71. data/app/assets/config/jobshop_manifest.js +0 -2
  72. data/app/assets/images/jobshop/logo.svg +0 -50
  73. data/app/assets/javascripts/jobshop/application.js +0 -34
  74. data/app/assets/stylesheets/jobshop/application.scss +0 -57
  75. data/app/assets/stylesheets/jobshop/breakpoints.scss +0 -47
  76. data/app/assets/stylesheets/jobshop/dialog.scss +0 -43
  77. data/app/controllers/concerns/registration_token_validation.rb +0 -41
  78. data/app/controllers/jobshop/teams/lookups_controller.rb +0 -20
  79. data/app/controllers/jobshop/teams_controller.rb +0 -6
  80. data/app/controllers/jobshop/welcome/places_controller.rb +0 -42
  81. data/app/controllers/jobshop/welcome/things_controller.rb +0 -42
  82. data/app/controllers/jobshop/welcome_controller.rb +0 -11
  83. data/app/helpers/jobshop/application_helper.rb +0 -14
  84. data/app/mailers/jobshop/teams_mailer.rb +0 -10
  85. data/app/models/jobshop/team.rb +0 -36
  86. data/app/policies/jobshop/application_policy.rb +0 -55
  87. data/app/views/devise/confirmations/new.html.haml +0 -10
  88. data/app/views/devise/mailer/confirmation_instructions.html.haml +0 -4
  89. data/app/views/devise/mailer/password_change.html.haml +0 -3
  90. data/app/views/devise/mailer/reset_password_instructions.html.haml +0 -6
  91. data/app/views/devise/mailer/unlock_instructions.html.haml +0 -5
  92. data/app/views/devise/passwords/edit.html.haml +0 -19
  93. data/app/views/devise/passwords/new.html.haml +0 -10
  94. data/app/views/devise/registrations/edit.html.haml +0 -31
  95. data/app/views/devise/registrations/new.html.haml +0 -21
  96. data/app/views/devise/sessions/new.html.haml +0 -15
  97. data/app/views/devise/shared/_links.html.haml +0 -19
  98. data/app/views/devise/unlocks/new.html.haml +0 -10
  99. data/app/views/jobshop/dashboards/show.html.haml +0 -140
  100. data/app/views/jobshop/places/show.html.haml +0 -12
  101. data/app/views/jobshop/session_activations/destroy.js.erb +0 -1
  102. data/app/views/jobshop/shared/_authenticated_header.html.haml +0 -8
  103. data/app/views/jobshop/shared/_unauthenticated_header.html.haml +0 -10
  104. data/app/views/jobshop/teams/lookups/show.html.haml +0 -14
  105. data/app/views/jobshop/teams_mailer/found_teams.html.haml +0 -11
  106. data/app/views/jobshop/teams_mailer/found_teams.text.erb +0 -8
  107. data/app/views/jobshop/welcome/index.html.haml +0 -21
  108. data/app/views/jobshop/welcome/places/new.html.haml +0 -22
  109. data/app/views/jobshop/welcome/registrations/new.html.haml +0 -32
  110. data/app/views/jobshop/welcome/things/new.html.haml +0 -24
  111. data/app/views/layouts/jobshop/application.html.haml +0 -15
  112. data/app/views/layouts/jobshop/unauthenticated.html.haml +0 -6
  113. data/config/initializers/assets.rb +0 -11
  114. data/config/initializers/devise.rb +0 -282
  115. data/config/initializers/field_with_errors.rb +0 -3
  116. data/config/initializers/simple_form.rb +0 -157
  117. data/config/locales/devise.en.yml +0 -62
  118. data/config/locales/simple_form.en.yml +0 -31
  119. data/db/migrate/keep +0 -0
  120. data/lib/generators/jobshop/team/USAGE +0 -7
  121. data/lib/generators/jobshop/team/team_generator.rb +0 -60
  122. data/lib/jobshop/failure_app.rb +0 -11
@@ -0,0 +1,42 @@
1
+ module Jobshop
2
+ class Inspection::Result < ApplicationRecord
3
+ FALSE_VALUES = [false, 0, "0", "f", "F", "false", "FALSE", "off", "OFF"]
4
+
5
+ self.primary_keys = [ :organization_id, :report_id, :criterion_id, :criterion_type, :position ]
6
+
7
+ belongs_to :organization
8
+ belongs_to :report, class_name: "Jobshop::Inspection::Report",
9
+ foreign_key: [ :organization_id, :report_id ]
10
+ belongs_to :criterion, polymorphic: true, optional: true, dependent: :destroy,
11
+ foreign_key: [ :organization_id, :report_id, :criterion_id, :criterion_type ]
12
+ belongs_to :tuple, class_name: "Jobshop::Inspection::Tuple",
13
+ foreign_key: [ :organization_id, :report_id, :position ]
14
+
15
+ def value
16
+ if self[:unit] == "boolean"
17
+ !!self[:value]
18
+ else
19
+ # binding.pry
20
+ self[:value] && Unitwise(self[:value].truncate(4), self[:unit])
21
+ end
22
+ end
23
+
24
+ def value=(value)
25
+ if criterion.unit == "boolean"
26
+ self[:value] = value
27
+ self[:unit] = "boolean"
28
+ else
29
+ if value.respond_to?(:unit)
30
+ self[:value] = value.value
31
+ self[:unit] = value.unit
32
+ else
33
+ self[:value] = value
34
+ end
35
+ end
36
+ end
37
+
38
+ def in_spec?
39
+ criterion.pass?(self[:value])
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module Jobshop
2
+ class Inspection::Tuple < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :report_id, :position ]
4
+
5
+ belongs_to :organization
6
+ belongs_to :report, class_name: "Jobshop::Inspection::Report", inverse_of: :tuples,
7
+ foreign_key: [ :organization_id, :report_id ]
8
+ has_many :results, class_name: "Jobshop::Inspection::Result", inverse_of: :tuple,
9
+ foreign_key: [ :organization_id, :report_id, :position ]
10
+
11
+ has_many :criteria, through: :results
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Jobshop
2
+ module Inspection
3
+ def self.table_name_prefix
4
+ "jobshop_inspection_"
5
+ end
6
+
7
+ CRITERION_TYPES = [
8
+ :inspection_boolean_criterion,
9
+ :inspection_limit_criterion,
10
+ :inspection_deviation_criterion
11
+ ].freeze
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ module Jobshop
2
+ class Order < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :order_id ]
4
+
5
+ after_create do
6
+ self.class.connection.clear_query_cache
7
+ @attributes = self.class.unscoped {
8
+ self.class.find_by!(organization: organization, number: number)
9
+ }.instance_variable_get(:@attributes)
10
+ @new_record = false
11
+ self
12
+ end
13
+
14
+ belongs_to :organization, inverse_of: :orders
15
+ belongs_to :created_by, class_name: "Jobshop::User",
16
+ foreign_key: [ :organization_id, :created_by_id ]
17
+ belongs_to :company, inverse_of: :orders,
18
+ foreign_key: [ :organization_id, :company_id ]
19
+ has_many :order_lines, inverse_of: :order,
20
+ foreign_key: [ :organization_id, :order_id ]
21
+
22
+ around_save :notify_on_save
23
+
24
+ def add_listener(listener)
25
+ (@listeners ||= []) << listener
26
+ end
27
+
28
+ def notify_listeners(event_name, *args)
29
+ @listeners && @listeners.each do |listener|
30
+ if listener.respond_to?(event_name)
31
+ listener.public_send(event_name, self, *args)
32
+ end
33
+ end
34
+ end
35
+
36
+ def notify_on_save
37
+ yield
38
+ notify_listeners(:on_save)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ module Jobshop
2
+ class OrderLine < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :order_id, :order_line_id]
4
+
5
+ belongs_to :organization
6
+ belongs_to :created_by, class_name: "Jobshop::User",
7
+ foreign_key: [ :organization_id, :created_by_id ]
8
+ belongs_to :order, inverse_of: :order_lines,
9
+ foreign_key: [ :organization_id, :order_id ]
10
+ belongs_to :product,
11
+ foreign_key: [ :organization_id, :product_id ]
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ module Jobshop
2
+ class Organization < ApplicationRecord
3
+ self.primary_key = :organization_id
4
+
5
+ has_many :users, inverse_of: :organization,
6
+ dependent: :restrict_with_exception
7
+ has_many :roles, inverse_of: :organization,
8
+ dependent: :restrict_with_exception
9
+ has_many :places, inverse_of: :organization,
10
+ dependent: :restrict_with_exception
11
+ has_many :collections, inverse_of: :organization,
12
+ dependent: :restrict_with_exception
13
+ has_many :things, inverse_of: :organization,
14
+ dependent: :restrict_with_exception
15
+ has_many :orders, inverse_of: :organization,
16
+ dependent: :restrict_with_exception
17
+ has_many :companies, inverse_of: :organization,
18
+ dependent: :restrict_with_exception
19
+ has_many :products, inverse_of: :organization,
20
+ dependent: :restrict_with_exception
21
+ has_many :routing_processes, inverse_of: :organization,
22
+ dependent: :restrict_with_exception
23
+
24
+ scope :grouped_by_email, ->(email_addresses) {
25
+ Jobshop::User
26
+ .where(email: email_addresses)
27
+ .joins(:organization)
28
+ .includes(:organization)
29
+ .each_with_object({}) { |user, organizations|
30
+ (organizations[user.email] ||= []) << user.organization
31
+ }
32
+ }
33
+ end
34
+ end
@@ -1,9 +1,11 @@
1
1
  module Jobshop
2
2
  class Place < ApplicationRecord
3
- belongs_to :team
3
+ self.primary_keys = [ :organization_id, :place_id ]
4
4
 
5
- validates(:name,
6
- uniqueness: { scope: :team_id, case_sensitive: false },
7
- presence: true)
5
+ belongs_to :organization, inverse_of: :places
6
+
7
+ validates :name,
8
+ presence: true,
9
+ uniqueness: { scope: :organization_id, case_sensitive: false }
8
10
  end
9
11
  end
@@ -0,0 +1,23 @@
1
+ module Jobshop
2
+ class Product < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :product_id ]
4
+
5
+ after_create do
6
+ self.class.connection.clear_query_cache
7
+ @attributes = self.class.unscoped {
8
+ self.class.find_by!(organization: organization, name: name)
9
+ }.instance_variable_get(:@attributes)
10
+ @new_record = false
11
+ self
12
+ end
13
+
14
+ belongs_to :organization, inverse_of: :products
15
+ belongs_to :created_by, class_name: "Jobshop::User",
16
+ foreign_key: [ :organization_id, :created_by_id ]
17
+ has_one :routing_process, inverse_of: :product,
18
+ foreign_key: [ :organization_id, :product_id ]
19
+ has_many :routing_steps, through: :routing_process
20
+
21
+ validates :name, presence: true
22
+ end
23
+ end
@@ -1,11 +1,17 @@
1
1
  module Jobshop
2
2
  class Registration < VirtualRecord
3
- memo_attr(:user) { team.users.build(user_params) }
4
- memo_attr(:team) {
5
- record = ::Jobshop::Team.find(params[:team_id])
6
-
7
- record.assign_attributes(team_params)
3
+ memo_attr(:user) {
4
+ record = organization.users.build(user_params)
5
+ record.singleton_class.class_eval do
6
+ validates :forename, presence: true
7
+ validates :surname, presence: true
8
+ end
9
+ record
10
+ }
8
11
 
12
+ memo_attr(:organization) {
13
+ record = ::Jobshop::Organization.find(params[:organization_id])
14
+ record.assign_attributes(organization_params)
9
15
  record.singleton_class.class_eval do
10
16
  validates :name, presence: true, on: :update, if: :name_changed?
11
17
  end
@@ -18,9 +24,7 @@ module Jobshop
18
24
 
19
25
  result = ActiveRecord::Base.transaction do
20
26
  user.save!
21
- team.update!(owner: user,
22
- registration_token: nil,
23
- registration_token_sent_at: nil)
27
+ organization.register_to(user)
24
28
  end
25
29
 
26
30
  result != false
@@ -32,8 +36,8 @@ module Jobshop
32
36
  params.fetch(:registration, ActionController::Parameters.new)
33
37
  end
34
38
 
35
- def team_params
36
- registration_params.fetch(:team, ActionController::Parameters.new)
39
+ def organization_params
40
+ registration_params.fetch(:organization, ActionController::Parameters.new)
37
41
  .permit(:name)
38
42
  end
39
43
 
@@ -0,0 +1,24 @@
1
+ module Jobshop
2
+ class Role < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :role_id ]
4
+
5
+ after_create do
6
+ self.class.connection.clear_query_cache
7
+
8
+ fresh_record = self.class.unscoped {
9
+ self.class.find_by!(organization: organization, name: name) }
10
+
11
+ @attributes = fresh_record.instance_variable_get(:@attributes)
12
+ @new_record = false
13
+ self
14
+ end
15
+
16
+ belongs_to :organization, inverse_of: :roles,
17
+ primary_key: [ :organization_id, :role_id ]
18
+
19
+ has_many :role_assignments, inverse_of: :role,
20
+ foreign_key: [ :organization_id, :role_id ]
21
+
22
+ validates :name, presence: true
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module Jobshop
2
+ class RoleAbility < ApplicationRecord
3
+ belongs_to :organization
4
+ belongs_to :role
5
+ belongs_to :ability
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Jobshop
2
+ class RoleAssignment < ApplicationRecord
3
+ belongs_to :organization
4
+ belongs_to :user, foreign_key: [ :organization_id, :user_id ]
5
+ belongs_to :role, foreign_key: [ :organization_id, :role_id ]
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ module Jobshop
2
+ class RoutingProcess < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :routing_process_id ]
4
+
5
+ after_create do
6
+ self.class.connection.clear_query_cache
7
+ @attributes = self.class.unscoped {
8
+ self.class.find_by!(organization: organization, product_id: product_id)
9
+ }.instance_variable_get(:@attributes)
10
+ @new_record = false
11
+ self
12
+ end
13
+
14
+ belongs_to :organization, inverse_of: :routing_processes
15
+ belongs_to :product, inverse_of: :routing_process,
16
+ foreign_key: [ :organization_id, :product_id ]
17
+ has_many :routing_steps, inverse_of: :routing_process,
18
+ foreign_key: [ :organization_id, :routing_process_id ]
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module Jobshop
2
+ class RoutingStep < ApplicationRecord
3
+ self.primary_keys = [ :organization_id, :routing_step_id ]
4
+
5
+ belongs_to :organization
6
+ belongs_to :routing_process, inverse_of: :routing_steps,
7
+ foreign_key: [ :organization_id, :routing_process_id ]
8
+
9
+ validates :name, presence: true
10
+ end
11
+ end
@@ -1,9 +1,35 @@
1
1
  module Jobshop
2
2
  class Thing < ApplicationRecord
3
- belongs_to :team
3
+ self.primary_keys = [ :organization_id, :thing_id ]
4
4
 
5
- validates(:identifier,
6
- uniqueness: { scope: :team_id, case_sensitive: false },
7
- presence: true)
5
+ after_create do
6
+ self.class.connection.clear_query_cache
7
+ fresh_thing = self.class.unscoped {
8
+ self.class.find_by!(organization: organization, collection: collection, name: name)
9
+ }
10
+ @attributes = fresh_thing.instance_variable_get(:@attributes)
11
+ @new_record = false
12
+ self
13
+ end
14
+
15
+ belongs_to :organization, inverse_of: :things
16
+ belongs_to :collection, inverse_of: :things,
17
+ foreign_key: [ :organization_id, :collection_id ]
18
+
19
+ validates :name, presence: true,
20
+ uniqueness: { scope: :organization_id, case_sensitive: false }
21
+
22
+ validates :custom_fields, json: { message: -> (errors) { errors },
23
+ schema: -> { my_schema },
24
+ options: { errors_as_objects: true } }
25
+
26
+ def my_schema
27
+ # TODO: Because shoulda-matchers doesn't properly recreate the test
28
+ # subject this has the return {} when collection is nil. Collection will
29
+ # only ever be nil during this test. Once the proper fk constraint is
30
+ # added to the db, the test in question will raise an exception and a new
31
+ # solution will need to be found.
32
+ collection && collection.schema || {}
33
+ end
8
34
  end
9
35
  end
@@ -1,34 +1,39 @@
1
1
  module Jobshop
2
2
  class User < ApplicationRecord
3
- # Include default devise modules. Others available are:
4
- # :confirmable, :lockable, :timeoutable and :omniauthable
5
- devise :database_authenticatable, :recoverable, :rememberable
3
+ self.primary_keys = :organization_id, :user_id
6
4
 
7
- belongs_to :team
8
- has_one :default_dashboard, class_name: "Jobshop::Dashboard", through: :team
9
- has_many :session_activations, dependent: :destroy
5
+ has_secure_password
6
+
7
+ after_create do
8
+ self.class.connection.clear_query_cache
9
+ @attributes = self.class.unscoped {
10
+ self.class.find_by!(organization: organization, email: email)
11
+ }.instance_variable_get(:@attributes)
12
+ @new_record = false
13
+ self
14
+ end
15
+
16
+ belongs_to :organization, inverse_of: :users
17
+ has_many :role_assignments, inverse_of: :user,
18
+ foreign_key: [ :organization_id, :user_id ]
19
+ has_many :roles, through: :role_assignments
20
+ has_many :sessions, inverse_of: :user, dependent: :destroy,
21
+ foreign_key: [ :organization_id, :user_id ]
10
22
 
11
23
  validates :email,
12
24
  presence: { if: :email_required? },
13
25
  format: { if: :email_required?, with: /\A[^@\s]+@[^@\s]+\z/ },
14
- uniqueness: { if: :email_changed?, scope: :team_id }
26
+ uniqueness: { if: :email_changed?, scope: :organization_id, case_sensitive: false }
27
+
28
+ validates :password_digest, presence: true
15
29
 
16
30
  validates :password,
17
31
  presence: { if: :password_required? },
18
32
  confirmation: { if: :password_required? }
19
33
 
20
- def self.find_for_authentication(warden_conditions)
21
- where(email: warden_conditions[:email],
22
- team_id: warden_conditions[:team_id]).first
23
- end
24
34
 
25
- def onboard?
26
- # TODO: Implement this correctly.
27
- false
28
- end
29
-
30
- def full_name
31
- @full_name = [ forename, surname ].join(" ")
35
+ def name
36
+ @name = [ forename, surname ].join(" ")
32
37
  end
33
38
 
34
39
  def activate_session
@@ -44,13 +49,6 @@ module Jobshop
44
49
  end
45
50
 
46
51
  private
47
- def generate_email_authentication_token
48
- loop do
49
- token = Devise.friendly_token
50
- break token unless Jobshop::User.where(email_authentication_token: token).first
51
- end
52
- end
53
-
54
52
  def password_required?
55
53
  !persisted? || !password.nil? || !password_confirmation.nil?
56
54
  end
@@ -0,0 +1,10 @@
1
+ module Jobshop
2
+ class TestUserSerializer
3
+ include FastJsonapi::ObjectSerializer
4
+ attributes :email, :forename, :surname, :job_title
5
+
6
+ attribute :organization_name do |object|
7
+ object.organization.name
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ module Jobshop
2
+ class AuthenticationService
3
+ attr_reader :email, :password
4
+
5
+ def initialize(email, password)
6
+ @email = email
7
+ @password = password
8
+ end
9
+
10
+ def perform
11
+ Jobshop::JWTService.encode(user_id: user.id) if user
12
+ end
13
+
14
+ private def user
15
+ user = User.find_by(email: email)
16
+ user && user.authenticate(password) ||
17
+ raise(AuthenticationHandler::InvalidCredentials)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ module Jobshop
2
+ class AuthorizationService
3
+ attr_reader :headers
4
+
5
+ def initialize(headers = {})
6
+ @headers = headers
7
+ end
8
+
9
+ def perform
10
+ { user: user }
11
+ end
12
+
13
+ private def user
14
+ @user ||= Jobshop::User.find(decoded_auth_token[:user_id]) if decoded_auth_token
15
+ rescue ActiveRecord::RecordNotFound => e
16
+ raise(AuthorizationHandler::TokenInvalid, e.message)
17
+ end
18
+
19
+ private def decoded_auth_token
20
+ @decoded_auth_token ||= JWTService.decode(http_auth_header)
21
+ end
22
+
23
+ private def http_auth_header
24
+ if headers["Authorization"].present?
25
+ return headers["Authorization"].split(" ").last
26
+ end
27
+ raise(AuthorizationHandler::TokenMissing)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module Jobshop
2
+ class JWTService
3
+ HMAC_SECRET = "fixme"
4
+
5
+ def self.encode(payload, exp = 24.hours.from_now)
6
+ payload[:exp] = exp.to_i
7
+ JWT.encode(payload, HMAC_SECRET)
8
+ end
9
+
10
+ def self.decode(token)
11
+ body = JWT.decode(token, HMAC_SECRET)[0]
12
+ HashWithIndifferentAccess.new body
13
+ rescue JWT::DecodeError => e
14
+ raise AuthorizationHandler::TokenInvalid, e.message
15
+ end
16
+ end
17
+ end
data/config/routes.rb CHANGED
@@ -1,45 +1,4 @@
1
1
  Jobshop::Engine.routes.draw do
2
- devise_for :users, class_name: "Jobshop::User", module: "devise",
3
- skip: :sessions
4
-
5
- unauthenticated do
6
- devise_scope :user do
7
- get "/sign_in/:team_id", to: "sessions#new", as: :new_user_session
8
- post "/sign_in/:team_id", to: "sessions#create", as: :user_session
9
- end
10
-
11
- get "/teams/lookup", to: "teams/lookups#show", as: :teams_lookup
12
- post "/teams/lookup", to: "teams/lookups#create"
13
-
14
- namespace :welcome do
15
- resource :registration, except: %w(edit destroy show update)
16
- end
17
-
18
- get "/", to: "dashboards#show"
19
- end
20
-
21
- authenticated do
22
- devise_scope :user do
23
- delete "/sign_out", to: "sessions#destroy", as: :destroy_user_session
24
- delete "/revoke/:id",
25
- to: "session_activations#destroy", as: :revoke_session
26
- end
27
-
28
- namespace :welcome do
29
- resource :things, except: %w(destroy show) do
30
- get "/", action: :index
31
- end
32
-
33
- resource :places, except: %w(destroy show) do
34
- get "/", action: :index
35
- end
36
- end
37
-
38
- get "/welcome", to: "welcome#index"
39
- get "/logins", to: "places#show"
40
-
41
- root to: "dashboards#show"
42
- end
43
-
44
- get "/about" => redirect("https://github.com/jobshop/jobshop"), as: :about
2
+ post "/signin", to: "sessions#new"
3
+ get "/current-user", to: "users#current"
45
4
  end
@@ -1,111 +1,6 @@
1
- class InitializeJobshop < ActiveRecord::Migration[5.0]
1
+ class InitializeJobshop < ActiveRecord::Migration[5.1]
2
2
  def change
3
3
  enable_extension "pgcrypto" unless extension_enabled? "pgcrypto"
4
4
  enable_extension "citext" unless extension_enabled? "citext"
5
-
6
- create_table(:jobshop_users, id: :uuid,
7
- default: -> { "gen_random_uuid()" }) do |t|
8
- ## Database authenticatable
9
- t.string :email, null: false, default: ""
10
- t.string :encrypted_password, null: false, default: ""
11
-
12
- ## Recoverable
13
- t.string :reset_password_token
14
- t.datetime :reset_password_sent_at
15
-
16
- ## Rememberable
17
- t.datetime :remember_created_at
18
-
19
- ## Trackable
20
- # t.integer :sign_in_count, default: 0, null: false
21
- # t.datetime :current_sign_in_at
22
- # t.datetime :last_sign_in_at
23
- # t.string :current_sign_in_ip
24
- # t.string :last_sign_in_ip
25
-
26
- ## Confirmable
27
- # t.string :confirmation_token
28
- # t.datetime :confirmed_at
29
- # t.datetime :confirmation_sent_at
30
- # t.string :unconfirmed_email # Only if using reconfirmable
31
-
32
- ## Lockable
33
- # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
34
- # t.string :unlock_token # Only if unlock strategy is :email or :both
35
- # t.datetime :locked_at
36
-
37
- t.uuid :team_id, null: false
38
- t.string :forename
39
- t.string :surname
40
- t.string :email_authentication_token
41
- t.datetime :email_authentication_token_sent_at, :datetime
42
- t.timestamps
43
- end
44
-
45
- add_index :jobshop_users, [ :email, :team_id ], unique: true
46
- add_index :jobshop_users, :reset_password_token, unique: true
47
- # add_index :jobshop_users, :confirmation_token, unique: true
48
- # add_index :jobshop_users, :unlock_token, unique: true
49
-
50
- create_table(:jobshop_teams, id: :uuid,
51
- default: -> { "gen_random_uuid()" }) do |t|
52
- t.string :name
53
- t.uuid :owner_id
54
- t.string :registration_token
55
- t.datetime :registration_token_sent_at
56
- t.timestamps
57
- end
58
-
59
- add_check :jobshop_teams, <<~CHECK, name: :check_owner_id_on_jobshop_teams
60
- ((owner_id IS NULL AND registration_token IS NOT NULL AND registration_token_sent_at IS NOT NULL) OR
61
- (owner_id IS NOT NULL AND registration_token IS NULL AND registration_token_sent_at IS NULL))
62
- CHECK
63
-
64
- add_index :jobshop_teams, [ "registration_token" ], unique: true
65
-
66
- add_foreign_key :jobshop_users,
67
- :jobshop_teams, column: "team_id", on_delete: :cascade
68
-
69
- add_foreign_key :jobshop_teams,
70
- :jobshop_users, column: "owner_id", on_delete: :restrict
71
-
72
- create_table(:jobshop_dashboards, id: :uuid,
73
- default: -> { "gen_random_uuid()" }) do |t|
74
- t.uuid :team_id
75
- t.timestamps
76
- end
77
-
78
- add_foreign_key :jobshop_dashboards,
79
- :jobshop_teams, column: "team_id", on_delete: :cascade
80
-
81
- create_table(:jobshop_session_activations, id: :uuid,
82
- default: -> { "gen_random_uuid()" }) do |t|
83
- t.uuid :user_id, null: false
84
- t.string :activation_token, null: false
85
- t.timestamps
86
- end
87
-
88
- add_index :jobshop_session_activations, :user_id
89
- add_index :jobshop_session_activations, :activation_token, unique: true
90
-
91
- create_table(:jobshop_things, id: :uuid,
92
- default: -> { "gen_random_uuid()" }) do |t|
93
- t.uuid :team_id, null: false
94
- t.citext :identifier
95
-
96
- t.timestamps
97
- end
98
-
99
- add_index :jobshop_things, [ :identifier, :team_id ], unique: true
100
-
101
- create_table(:jobshop_places, id: :uuid,
102
- default: -> { "gen_random_uuid()" }) do |t|
103
- t.uuid :team_id, null: false
104
- t.citext :name
105
-
106
- t.timestamps
107
- end
108
-
109
- add_index :jobshop_places, [ :name, :team_id ], unique: true
110
5
  end
111
6
  end