jobshop 0.0.131 → 0.0.157
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +16 -9
- data/app/controllers/concerns/jobshop/authentication_handler.rb +15 -0
- data/app/controllers/concerns/jobshop/authorization_handler.rb +29 -0
- data/app/controllers/jobshop/application_controller.rb +5 -32
- data/app/controllers/jobshop/organizations/lookups_controller.rb +18 -0
- data/app/controllers/jobshop/places_controller.rb +1 -1
- data/app/controllers/jobshop/{welcome/registrations_controller.rb → registrations_controller.rb} +2 -7
- data/app/controllers/jobshop/session_activations_controller.rb +1 -1
- data/app/controllers/jobshop/sessions_controller.rb +15 -1
- data/app/controllers/jobshop/users_controller.rb +9 -0
- data/app/mailers/jobshop/application_mailer.rb +2 -2
- data/app/models/jobshop/ability.rb +7 -0
- data/app/models/jobshop/collection.rb +22 -0
- data/app/models/jobshop/company.rb +22 -0
- data/app/models/jobshop/dashboard.rb +1 -1
- data/app/models/jobshop/inspection/boolean_criterion.rb +44 -0
- data/app/models/jobshop/inspection/criterion.rb +22 -0
- data/app/models/jobshop/inspection/deviation_criterion.rb +112 -0
- data/app/models/jobshop/inspection/limit_criterion.rb +114 -0
- data/app/models/jobshop/inspection/report.rb +41 -0
- data/app/models/jobshop/inspection/result.rb +42 -0
- data/app/models/jobshop/inspection/tuple.rb +13 -0
- data/app/models/jobshop/inspection.rb +13 -0
- data/app/models/jobshop/order.rb +41 -0
- data/app/models/jobshop/order_line.rb +13 -0
- data/app/models/jobshop/organization.rb +34 -0
- data/app/models/jobshop/place.rb +6 -4
- data/app/models/jobshop/product.rb +23 -0
- data/app/models/jobshop/registration.rb +14 -10
- data/app/models/jobshop/role.rb +24 -0
- data/app/models/jobshop/role_ability.rb +7 -0
- data/app/models/jobshop/role_assignment.rb +7 -0
- data/app/models/jobshop/routing_process.rb +20 -0
- data/app/models/jobshop/routing_step.rb +11 -0
- data/app/models/jobshop/thing.rb +30 -4
- data/app/models/jobshop/user.rb +23 -25
- data/app/serializers/jobshop/test_user_serializer.rb +10 -0
- data/app/services/jobshop/authentication_service.rb +20 -0
- data/app/services/jobshop/authorization_service.rb +30 -0
- data/app/services/jobshop/jwt_service.rb +17 -0
- data/config/routes.rb +2 -43
- data/db/migrate/20170311194758_initialize_jobshop.rb +1 -106
- data/db/migrate/20171216021339_create_organizations.rb +20 -0
- data/db/migrate/20171216021717_create_users.rb +30 -0
- data/db/migrate/20171216021853_create_companies.rb +25 -0
- data/db/migrate/20171216022020_create_places.rb +22 -0
- data/db/migrate/20171216022135_create_products.rb +27 -0
- data/db/migrate/20171216022605_create_orders.rb +53 -0
- data/db/migrate/20171216023018_create_roles.rb +52 -0
- data/db/migrate/20171216023022_create_sessions.rb +21 -0
- data/db/migrate/20171216035357_create_things.rb +44 -0
- data/db/migrate/20171219022118_create_routing_processes.rb +57 -0
- data/db/migrate/20180107203241_create_inspection.rb +159 -0
- data/{app/assets/stylesheets/jobshop/welcome.scss → db/seeds.rb} +0 -0
- data/lib/generators/jobshop/app/app_generator.rb +36 -3
- data/lib/generators/jobshop/app/templates/config/database.yml.tt +19 -0
- data/lib/generators/jobshop/config/templates/config/initializers/jobshop.rb.tt +0 -2
- data/lib/generators/jobshop/dummy/dummy_generator.rb +3 -14
- data/lib/jobshop/cli/spinner.rb +21 -0
- data/lib/jobshop/cli.rb +44 -24
- data/lib/jobshop/configuration.rb +24 -0
- data/lib/jobshop/dummy_app.rb +155 -0
- data/lib/jobshop/engine.rb +30 -31
- data/lib/jobshop/helpers/migration.rb +55 -0
- data/lib/jobshop/version.rb +2 -2
- data/lib/jobshop.rb +7 -21
- data/lib/tasks/jobshop_tasks.rake +7 -24
- metadata +113 -150
- data/app/assets/config/jobshop_manifest.js +0 -2
- data/app/assets/images/jobshop/logo.svg +0 -50
- data/app/assets/javascripts/jobshop/application.js +0 -34
- data/app/assets/stylesheets/jobshop/application.scss +0 -57
- data/app/assets/stylesheets/jobshop/breakpoints.scss +0 -47
- data/app/assets/stylesheets/jobshop/dialog.scss +0 -43
- data/app/controllers/concerns/registration_token_validation.rb +0 -41
- data/app/controllers/jobshop/teams/lookups_controller.rb +0 -20
- data/app/controllers/jobshop/teams_controller.rb +0 -6
- data/app/controllers/jobshop/welcome/places_controller.rb +0 -42
- data/app/controllers/jobshop/welcome/things_controller.rb +0 -42
- data/app/controllers/jobshop/welcome_controller.rb +0 -11
- data/app/helpers/jobshop/application_helper.rb +0 -14
- data/app/mailers/jobshop/teams_mailer.rb +0 -10
- data/app/models/jobshop/team.rb +0 -36
- data/app/policies/jobshop/application_policy.rb +0 -55
- data/app/views/devise/confirmations/new.html.haml +0 -10
- data/app/views/devise/mailer/confirmation_instructions.html.haml +0 -4
- data/app/views/devise/mailer/password_change.html.haml +0 -3
- data/app/views/devise/mailer/reset_password_instructions.html.haml +0 -6
- data/app/views/devise/mailer/unlock_instructions.html.haml +0 -5
- data/app/views/devise/passwords/edit.html.haml +0 -19
- data/app/views/devise/passwords/new.html.haml +0 -10
- data/app/views/devise/registrations/edit.html.haml +0 -31
- data/app/views/devise/registrations/new.html.haml +0 -21
- data/app/views/devise/sessions/new.html.haml +0 -15
- data/app/views/devise/shared/_links.html.haml +0 -19
- data/app/views/devise/unlocks/new.html.haml +0 -10
- data/app/views/jobshop/dashboards/show.html.haml +0 -140
- data/app/views/jobshop/places/show.html.haml +0 -12
- data/app/views/jobshop/session_activations/destroy.js.erb +0 -1
- data/app/views/jobshop/shared/_authenticated_header.html.haml +0 -8
- data/app/views/jobshop/shared/_unauthenticated_header.html.haml +0 -10
- data/app/views/jobshop/teams/lookups/show.html.haml +0 -14
- data/app/views/jobshop/teams_mailer/found_teams.html.haml +0 -11
- data/app/views/jobshop/teams_mailer/found_teams.text.erb +0 -8
- data/app/views/jobshop/welcome/index.html.haml +0 -21
- data/app/views/jobshop/welcome/places/new.html.haml +0 -22
- data/app/views/jobshop/welcome/registrations/new.html.haml +0 -32
- data/app/views/jobshop/welcome/things/new.html.haml +0 -24
- data/app/views/layouts/jobshop/application.html.haml +0 -15
- data/app/views/layouts/jobshop/unauthenticated.html.haml +0 -6
- data/config/initializers/assets.rb +0 -11
- data/config/initializers/devise.rb +0 -282
- data/config/initializers/field_with_errors.rb +0 -3
- data/config/initializers/simple_form.rb +0 -157
- data/config/locales/devise.en.yml +0 -62
- data/config/locales/simple_form.en.yml +0 -31
- data/db/migrate/keep +0 -0
- data/lib/generators/jobshop/team/USAGE +0 -7
- data/lib/generators/jobshop/team/team_generator.rb +0 -60
- 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,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
|
data/app/models/jobshop/place.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Jobshop
|
2
2
|
class Place < ApplicationRecord
|
3
|
-
|
3
|
+
self.primary_keys = [ :organization_id, :place_id ]
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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) {
|
4
|
-
|
5
|
-
record
|
6
|
-
|
7
|
-
|
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
|
-
|
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
|
36
|
-
registration_params.fetch(:
|
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,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
|
data/app/models/jobshop/thing.rb
CHANGED
@@ -1,9 +1,35 @@
|
|
1
1
|
module Jobshop
|
2
2
|
class Thing < ApplicationRecord
|
3
|
-
|
3
|
+
self.primary_keys = [ :organization_id, :thing_id ]
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
data/app/models/jobshop/user.rb
CHANGED
@@ -1,34 +1,39 @@
|
|
1
1
|
module Jobshop
|
2
2
|
class User < ApplicationRecord
|
3
|
-
|
4
|
-
# :confirmable, :lockable, :timeoutable and :omniauthable
|
5
|
-
devise :database_authenticatable, :recoverable, :rememberable
|
3
|
+
self.primary_keys = :organization_id, :user_id
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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: :
|
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
|
26
|
-
|
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,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
|
-
|
3
|
-
|
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.
|
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
|