mno-enterprise-core 2.0.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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/Rakefile +12 -0
  4. data/app/assets/images/mno_enterprise/main-logo.png +0 -0
  5. data/app/controllers/mno_enterprise/application_controller.rb +116 -0
  6. data/app/helpers/mno_enterprise/application_helper.rb +67 -0
  7. data/app/helpers/mno_enterprise/impersonate_helper.rb +27 -0
  8. data/app/models/mno_enterprise/ability.rb +6 -0
  9. data/app/models/mno_enterprise/app.rb +72 -0
  10. data/app/models/mno_enterprise/app_instance.rb +36 -0
  11. data/app/models/mno_enterprise/app_instances_sync.rb +6 -0
  12. data/app/models/mno_enterprise/arrears_situation.rb +6 -0
  13. data/app/models/mno_enterprise/audit_event.rb +21 -0
  14. data/app/models/mno_enterprise/base_resource.rb +228 -0
  15. data/app/models/mno_enterprise/credit_card.rb +40 -0
  16. data/app/models/mno_enterprise/deletion_request.rb +35 -0
  17. data/app/models/mno_enterprise/impac/dashboard.rb +36 -0
  18. data/app/models/mno_enterprise/impac/dashboard_provisioner.rb +5 -0
  19. data/app/models/mno_enterprise/impac/kpi.rb +9 -0
  20. data/app/models/mno_enterprise/impac/widget.rb +13 -0
  21. data/app/models/mno_enterprise/invoice.rb +53 -0
  22. data/app/models/mno_enterprise/org_invite.rb +50 -0
  23. data/app/models/mno_enterprise/organization.rb +33 -0
  24. data/app/models/mno_enterprise/team.rb +50 -0
  25. data/app/models/mno_enterprise/tenant.rb +5 -0
  26. data/app/models/mno_enterprise/tenant_invoice.rb +5 -0
  27. data/app/models/mno_enterprise/user.rb +183 -0
  28. data/app/pdf/mno_enterprise/invoice_pdf.rb +516 -0
  29. data/config/initializers/audit_log.rb +5 -0
  30. data/config/locales/devise.en.yml +60 -0
  31. data/config/routes.rb +2 -0
  32. data/config/styleguide.yml +106 -0
  33. data/lib/accountingjs_serializer.rb +51 -0
  34. data/lib/devise/controllers/extension_helpers.rb +52 -0
  35. data/lib/devise/extension_routes.rb +11 -0
  36. data/lib/devise/hooks/password_expirable.rb +5 -0
  37. data/lib/devise/models/password_expirable.rb +28 -0
  38. data/lib/devise/models/remote_authenticatable.rb +48 -0
  39. data/lib/devise/strategies/remote_authenticatable.rb +44 -0
  40. data/lib/devise_extension.rb +36 -0
  41. data/lib/faraday/adapter/net_http_no_proxy.rb +19 -0
  42. data/lib/generators/mno_enterprise/database_extension/USAGE +11 -0
  43. data/lib/generators/mno_enterprise/database_extension/database_extension_generator.rb +36 -0
  44. data/lib/generators/mno_enterprise/database_extension/templates/model.rb +9 -0
  45. data/lib/generators/mno_enterprise/dummy/dummy_generator.rb +98 -0
  46. data/lib/generators/mno_enterprise/dummy/templates/rails/application.rb.erb +9 -0
  47. data/lib/generators/mno_enterprise/dummy/templates/rails/boot.rb.erb +6 -0
  48. data/lib/generators/mno_enterprise/dummy/templates/rails/database.yml +22 -0
  49. data/lib/generators/mno_enterprise/dummy/templates/rails/routes.rb +8 -0
  50. data/lib/generators/mno_enterprise/dummy/templates/rails/test-env.rb +45 -0
  51. data/lib/generators/mno_enterprise/install/install_generator.rb +140 -0
  52. data/lib/generators/mno_enterprise/install/templates/Procfile +1 -0
  53. data/lib/generators/mno_enterprise/install/templates/config/initializers/mno_enterprise.rb +135 -0
  54. data/lib/generators/mno_enterprise/install/templates/config/mno_enterprise_styleguide.yml +104 -0
  55. data/lib/generators/mno_enterprise/install/templates/javascripts/mno_enterprise_extensions.js +7 -0
  56. data/lib/generators/mno_enterprise/install/templates/stylesheets/main.less_erb +25 -0
  57. data/lib/generators/mno_enterprise/install/templates/stylesheets/theme.less_erb +59 -0
  58. data/lib/generators/mno_enterprise/install/templates/stylesheets/variables.less +337 -0
  59. data/lib/generators/mno_enterprise/install/templates/tasks/sprites.rake +14 -0
  60. data/lib/generators/mno_enterprise/puma_stack/puma_stack_generator.rb +58 -0
  61. data/lib/generators/mno_enterprise/templates/scripts/monit/app-server.conf +8 -0
  62. data/lib/generators/mno_enterprise/templates/scripts/nginx/app +51 -0
  63. data/lib/generators/mno_enterprise/templates/scripts/puma.rb +25 -0
  64. data/lib/generators/mno_enterprise/templates/scripts/setup.sh +27 -0
  65. data/lib/generators/mno_enterprise/templates/scripts/upstart/app-web-hotrestart.conf +26 -0
  66. data/lib/generators/mno_enterprise/templates/scripts/upstart/app-web-server.conf +34 -0
  67. data/lib/generators/mno_enterprise/templates/scripts/upstart/app-web.conf +2 -0
  68. data/lib/generators/mno_enterprise/templates/scripts/upstart/app.conf +11 -0
  69. data/lib/her_extension/her_orm_adapter.rb +54 -0
  70. data/lib/her_extension/middleware/mnoe_api_v1_parse_json.rb +54 -0
  71. data/lib/her_extension/model/associations/association.rb +61 -0
  72. data/lib/her_extension/model/associations/association_proxy.rb +34 -0
  73. data/lib/her_extension/model/associations/has_many_association.rb +115 -0
  74. data/lib/her_extension/model/attributes.rb +43 -0
  75. data/lib/her_extension/model/orm.rb +59 -0
  76. data/lib/her_extension/model/parse.rb +40 -0
  77. data/lib/her_extension/model/relation.rb +92 -0
  78. data/lib/her_extension/validations/remote_uniqueness_validation.rb +33 -0
  79. data/lib/html_processor.rb +106 -0
  80. data/lib/mandrill_client.rb +58 -0
  81. data/lib/mno-enterprise-core.rb +1 -0
  82. data/lib/mno_enterprise/concerns.rb +4 -0
  83. data/lib/mno_enterprise/concerns/controllers.rb +6 -0
  84. data/lib/mno_enterprise/concerns/controllers/angular_csrf.rb +59 -0
  85. data/lib/mno_enterprise/concerns/controllers/auth.rb +9 -0
  86. data/lib/mno_enterprise/concerns/controllers/auth/confirmations_controller.rb +187 -0
  87. data/lib/mno_enterprise/concerns/controllers/auth/passwords_controller.rb +54 -0
  88. data/lib/mno_enterprise/concerns/controllers/auth/registrations_controller.rb +136 -0
  89. data/lib/mno_enterprise/concerns/controllers/auth/sessions_controller.rb +54 -0
  90. data/lib/mno_enterprise/concerns/controllers/auth/unlocks_controller.rb +50 -0
  91. data/lib/mno_enterprise/concerns/models.rb +6 -0
  92. data/lib/mno_enterprise/concerns/models/ability.rb +108 -0
  93. data/lib/mno_enterprise/concerns/models/app_instance.rb +100 -0
  94. data/lib/mno_enterprise/concerns/models/organization.rb +102 -0
  95. data/lib/mno_enterprise/core.rb +279 -0
  96. data/lib/mno_enterprise/database_extendable.rb +57 -0
  97. data/lib/mno_enterprise/engine.rb +33 -0
  98. data/lib/mno_enterprise/testing_support/ability_test_helper.rb +10 -0
  99. data/lib/mno_enterprise/testing_support/common_rake.rb +19 -0
  100. data/lib/mno_enterprise/testing_support/factories.rb +13 -0
  101. data/lib/mno_enterprise/testing_support/factories/app_instances.rb +30 -0
  102. data/lib/mno_enterprise/testing_support/factories/apps.rb +45 -0
  103. data/lib/mno_enterprise/testing_support/factories/arrears_situation.rb +14 -0
  104. data/lib/mno_enterprise/testing_support/factories/audit_event.rb +15 -0
  105. data/lib/mno_enterprise/testing_support/factories/credit_card.rb +33 -0
  106. data/lib/mno_enterprise/testing_support/factories/deletion_request.rb +17 -0
  107. data/lib/mno_enterprise/testing_support/factories/impac/dashboards.rb +15 -0
  108. data/lib/mno_enterprise/testing_support/factories/impac/kpis.rb +20 -0
  109. data/lib/mno_enterprise/testing_support/factories/impac/widgets.rb +15 -0
  110. data/lib/mno_enterprise/testing_support/factories/invoices.rb +51 -0
  111. data/lib/mno_enterprise/testing_support/factories/org_invite.rb +24 -0
  112. data/lib/mno_enterprise/testing_support/factories/organizations.rb +25 -0
  113. data/lib/mno_enterprise/testing_support/factories/team.rb +17 -0
  114. data/lib/mno_enterprise/testing_support/factories/tenant.rb +12 -0
  115. data/lib/mno_enterprise/testing_support/factories/tenant_invoice.rb +29 -0
  116. data/lib/mno_enterprise/testing_support/factories/users.rb +48 -0
  117. data/lib/mno_enterprise/testing_support/jpi_v1_test_helper.rb +49 -0
  118. data/lib/mno_enterprise/testing_support/mno_enterprise_api_test_helper.rb +167 -0
  119. data/lib/mno_enterprise/testing_support/mnoe_faraday_test_adapter.rb +173 -0
  120. data/lib/mno_enterprise/testing_support/organizations_shared_helpers.rb +175 -0
  121. data/lib/mno_enterprise/testing_support/user_action_shared.rb +47 -0
  122. data/lib/mno_enterprise/version.rb +3 -0
  123. data/lib/tasks/mno_enterprise_tasks.rake +22 -0
  124. data/spec/controllers/mno_enterprise/angular_csrf_spec.rb +42 -0
  125. data/spec/lib/her_extension/her_orm_adapter.rb +7 -0
  126. data/spec/lib/her_extension/model/relation_spec.rb +7 -0
  127. data/spec/lib/mandrill_client_spec.rb +64 -0
  128. data/spec/mno_enterprise_spec.rb +79 -0
  129. data/spec/models/mno_enterprise/app_instance_spec.rb +7 -0
  130. data/spec/models/mno_enterprise/app_spec.rb +62 -0
  131. data/spec/models/mno_enterprise/base_resource_spec.rb +28 -0
  132. data/spec/models/mno_enterprise/deletion_request_spec.rb +26 -0
  133. data/spec/models/mno_enterprise/invoice_spec.rb +7 -0
  134. data/spec/models/mno_enterprise/organization_spec.rb +7 -0
  135. data/spec/models/mno_enterprise/user_spec.rb +44 -0
  136. data/spec/rails_helper.rb +73 -0
  137. data/spec/spec_helper.rb +78 -0
  138. metadata +421 -0
@@ -0,0 +1,40 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/credit_cards
5
+ # - /v1/organizations/:organization_id/credit_card
6
+ #
7
+ # id :integer not null, primary key
8
+ # title :string(255)
9
+ # first_name :string(255)
10
+ # last_name :string(255)
11
+ # country :string(255)
12
+ # masked_number :string(255)
13
+ # month :integer
14
+ # year :integer
15
+ # user_id :integer
16
+ # token :string(255)
17
+ # created_at :datetime not null
18
+ # updated_at :datetime not null
19
+ # owner_id :integer
20
+ # owner_type :string(255)
21
+ # billing_address :text
22
+ # billing_city :string(255)
23
+ # billing_postcode :string(255)
24
+ # billing_country :string(255)
25
+ # duplicated :boolean default(FALSE)
26
+ #
27
+
28
+ module MnoEnterprise
29
+ class CreditCard < BaseResource
30
+
31
+ attributes :id, :created_at, :updated_at, :title, :first_name, :last_name, :country, :masked_number, :number,
32
+ :month, :year, :billing_address, :billing_city, :billing_postcode, :billing_country, :verification_value, :organization_id
33
+
34
+ #==============================================================
35
+ # Associations
36
+ #==============================================================
37
+ belongs_to :organization, class_name: 'MnoEnterprise::Organization'
38
+
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ # == Schema Information
2
+ #
3
+ # Table name: deletion_requests
4
+ #
5
+ # id :integer not null, primary key
6
+ # token :string(255)
7
+ # status :string(255)
8
+ # created_at :datetime not null
9
+ # updated_at :datetime not null
10
+ #
11
+
12
+ module MnoEnterprise
13
+ class DeletionRequest < BaseResource
14
+ attributes :id, :token, :status, :user_id
15
+
16
+ #==============================================================
17
+ # Associations
18
+ #==============================================================
19
+ belongs_to :user, class_name: 'MnoEnterprise::User'
20
+
21
+ #============================================
22
+ # Instance methods
23
+ #============================================
24
+ # We want to use the token instead of the id
25
+ def to_param
26
+ self.token
27
+ end
28
+
29
+ # TODO: specs
30
+ # Freeze user acocunt and update the deletion request
31
+ def freeze_account!
32
+ self.put(operation: 'freeze')
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ module MnoEnterprise
2
+ class Impac::Dashboard < BaseResource
3
+
4
+ attributes :name, :widgets_order, :organization_ids, :widgets_templates, :currency
5
+
6
+ has_many :widgets, class_name: 'MnoEnterprise::Impac::Widget', dependent: :destroy
7
+ has_many :kpis, class_name: 'MnoEnterprise::Impac::Kpi', dependent: :destroy
8
+ belongs_to :owner, polymorphic: true
9
+
10
+ #============================================
11
+ # Instance methods
12
+ #============================================
13
+ # Return the full name of this dashboard
14
+ # Currently a simple accessor to the dashboard name (used to include the company name)
15
+ def full_name
16
+ self.name
17
+ end
18
+
19
+ # Return all the organizations linked to this dashboard and to which
20
+ # the user has access
21
+ def organizations
22
+ self.organization_ids.map do |uid|
23
+ MnoEnterprise::Organization.find_by(uid: uid)
24
+ end
25
+ end
26
+
27
+ def sorted_widgets
28
+ order = self.widgets_order.map(&:to_i) | self.widgets.map{|w| w.id }
29
+ order.map { |id| self.widgets.to_a.find{ |w| w.id == id} }.compact
30
+ end
31
+
32
+ def to_audit_event
33
+ name
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ module MnoEnterprise
2
+ class Impac::DashboardProvisioner < BaseResource
3
+ attributes :dashboards
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ module MnoEnterprise
2
+ class Impac::Kpi < BaseResource
3
+
4
+ attributes :name, :settings, :targets, :extra_params, :endpoint, :source, :element_watched
5
+
6
+ belongs_to :dashboard, class_name: 'MnoEnterprise::Impac::Dashboard'
7
+
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module MnoEnterprise
2
+ class Impac::Widget < BaseResource
3
+
4
+ attributes :name, :width, :widget_category, :settings
5
+
6
+ belongs_to :dashboard, class_name: 'MnoEnterprise::Impac::Dashboard'
7
+
8
+ def to_audit_event
9
+ name
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/invoices
5
+ # - /v1/organizations/:organization_id/invoices
6
+ #
7
+ # id :integer not null, primary key
8
+ # price_cents :integer
9
+ # currency :string(255)
10
+ # invoicable_type :string(255)
11
+ # invoicable_id :integer
12
+ # started_at :datetime
13
+ # ended_at :datetime
14
+ # created_at :datetime not null
15
+ # updated_at :datetime not null
16
+ # paid_at :datetime
17
+ # pdf :string(255)
18
+ # payment_id :integer
19
+ # transferred_from_id :integer
20
+ # transferred_from_type :string(255)
21
+ # transferred_at :datetime
22
+ # account_transaction_id :integer
23
+ # resolver_invoice_id :integer
24
+ # resolving_invoice_id :integer
25
+ # slug :string(255)
26
+ # promo_voucher_id :integer
27
+ # tax_pips_applied :integer
28
+ # billing_address :text
29
+ # partner_invoice_id :integer
30
+ # mnoe_tenant_id :integer
31
+ #
32
+
33
+ module MnoEnterprise
34
+ class Invoice < BaseResource
35
+ #==============================================================
36
+ # Associations
37
+ #==============================================================
38
+ belongs_to :organization, class_name: 'MnoEnterprise::Organization'
39
+
40
+ # Return a label describing the time period
41
+ # this invoice covers
42
+ def period_label
43
+ return '' unless self.started_at && self.ended_at
44
+ "#{self.started_at.strftime("%b %d,%Y")} to #{self.ended_at.strftime("%b %d,%Y")}"
45
+ end
46
+
47
+ # Return true if the invoice has been paid
48
+ # false otherwise
49
+ def paid?
50
+ !self.paid_at.blank?
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,50 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/org_invites
5
+ # - /v1/organizations/:organization_id/org_invites
6
+ #
7
+ # id :integer not null, primary key
8
+ # user_id :integer
9
+ # user_email :string(255)
10
+ # organization_id :integer
11
+ # referrer_id :integer
12
+ # token :string(255)
13
+ # status :string(255)
14
+ # created_at :datetime not null
15
+ # updated_at :datetime not null
16
+ # user_role :string(255)
17
+ # team_id :integer
18
+ #
19
+
20
+ module MnoEnterprise
21
+ class OrgInvite < BaseResource
22
+ scope :active, -> { where(status: 'pending') }
23
+
24
+ #==============================================================
25
+ # Associations
26
+ #==============================================================
27
+ belongs_to :user, class_name: 'MnoEnterprise::User'
28
+ belongs_to :referrer, class_name: 'MnoEnterprise::User'
29
+ belongs_to :organization, class_name: 'MnoEnterprise::Organization'
30
+ belongs_to :team, class_name: 'MnoEnterprise::Team'
31
+
32
+ # TODO: specs
33
+ # Add the user to the organization and update the status of the invite
34
+ # Add team
35
+ def accept!(user = self.user)
36
+ self.put(operation: 'accept', data: { user_id: user.id})
37
+ end
38
+
39
+ # TODO: specs
40
+ def cancel!
41
+ self.put(operation: 'cancel')
42
+ end
43
+
44
+ # TODO: specs
45
+ # Check whether the invite is expired or not
46
+ def expired?
47
+ self.status != 'pending' || self.created_at < 3.days.ago
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,33 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/organizations
5
+ # - /v1/users/:user_id/organizations
6
+ #
7
+ # id :integer not null, primary key
8
+ # uid :string(255)
9
+ # name :string(255)
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
12
+ # account_frozen :boolean default(FALSE)
13
+ # free_trial_end_at :datetime
14
+ # soa_enabled :boolean default(TRUE)
15
+ # mails :text
16
+ # logo :string(255)
17
+ # latitude :float default(0.0)
18
+ # longitude :float default(0.0)
19
+ # geo_country_code :string(255)
20
+ # geo_state_code :string(255)
21
+ # geo_city :string(255)
22
+ # geo_tz :string(255)
23
+ # geo_currency :string(255)
24
+ # meta_data :text
25
+ # industry :string(255)
26
+ # size :string(255)
27
+ #
28
+
29
+ module MnoEnterprise
30
+ class Organization < BaseResource
31
+ include MnoEnterprise::Concerns::Models::Organization
32
+ end
33
+ end
@@ -0,0 +1,50 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/teams
5
+ # - /v1/organizations/:organization_id/teams
6
+ #
7
+ # id :integer not null, primary key
8
+ # name :string(255)
9
+ # created_at :datetime not null
10
+ # updated_at :datetime not null
11
+ # organization_id :integer
12
+ #
13
+
14
+ module MnoEnterprise
15
+ class Team < BaseResource
16
+
17
+ attributes :id, :name, :organization_id
18
+
19
+ #=====================================
20
+ # Associations
21
+ #=====================================
22
+ belongs_to :organization, class_name: 'MnoEnterprise::Organization'
23
+ has_many :users, class_name: 'MnoEnterprise::User'
24
+ has_many :app_instances, class_name: 'MnoEnterprise::AppInstance'
25
+
26
+
27
+ # Add a user to the team
28
+ # TODO: specs
29
+ def add_user(user)
30
+ self.users.create(id: user.id)
31
+ end
32
+
33
+ # Remove a user from the team
34
+ # TODO: specs
35
+ def remove_user(user)
36
+ self.users.destroy(id: user.id)
37
+ end
38
+
39
+ # Set the app_instance permissions of this team
40
+ # Accept a collection of hashes or an array of ids
41
+ # TODO: specs
42
+ def set_access_to(collection_or_array)
43
+ # Empty arrays do not seem to be passed in the request. Force value in this case
44
+ list = collection_or_array.empty? ? [""] : collection_or_array
45
+ self.put(data: { set_access_to: list })
46
+ self.reload
47
+ self
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module MnoEnterprise
2
+ class Tenant < BaseResource
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module MnoEnterprise
2
+ class TenantInvoice < BaseResource
3
+
4
+ end
5
+ end
@@ -0,0 +1,183 @@
1
+ # == Schema Information
2
+ #
3
+ # Endpoint:
4
+ # - /v1/users
5
+ # - /v1/organizations/:organization_id/users
6
+ #
7
+ # id :string e.g.: 1
8
+ # uid :string e.g.: usr-k3j23npo
9
+ # email :string(255) default(""), not null
10
+ # authenticatable_salt :string(255) used for session authentication
11
+ # encrypted_password :string(255) default(""), not null
12
+ # reset_password_token :string(255)
13
+ # reset_password_sent_at :datetime
14
+ # remember_created_at :datetime
15
+ # sign_in_count :integer default(0)
16
+ # current_sign_in_at :datetime
17
+ # last_sign_in_at :datetime
18
+ # current_sign_in_ip :string(255)
19
+ # last_sign_in_ip :string(255)
20
+ # confirmation_token :string(255)
21
+ # confirmed_at :datetime
22
+ # confirmation_sent_at :datetime
23
+ # unconfirmed_email :string(255)
24
+ # failed_attempts :integer default(0)
25
+ # unlock_token :string(255)
26
+ # locked_at :datetime
27
+ # created_at :datetime not null
28
+ # updated_at :datetime not null
29
+ # name :string(255)
30
+ # surname :string(255)
31
+ # company :string(255)
32
+ # phone :string(255)
33
+ # phone_country_code :string(255)
34
+ # geo_country_code :string(255)
35
+ # geo_state_code :string(255)
36
+ # geo_city :string(255)
37
+ # website :string(255)
38
+ #
39
+
40
+ module MnoEnterprise
41
+ class User < BaseResource
42
+ extend Devise::Models
43
+
44
+ # Note: password and encrypted_password are write-only attributes and are never returned by
45
+ # the remote API. If you are looking for a session token, use authenticatable_salt
46
+ attributes :uid, :email, :password, :current_password, :password_confirmation, :authenticatable_salt, :encrypted_password, :reset_password_token, :reset_password_sent_at,
47
+ :remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
48
+ :last_sign_in_ip, :confirmation_token, :confirmed_at, :confirmation_sent_at, :unconfirmed_email,
49
+ :failed_attempts, :unlock_token, :locked_at, :name, :surname, :company, :phone, :phone_country_code,
50
+ :geo_country_code, :geo_state_code, :geo_city, :website, :orga_on_create, :sso_session, :current_password_required
51
+
52
+ define_model_callbacks :validation #required by Devise
53
+ devise :remote_authenticatable, :registerable, :recoverable, :rememberable,
54
+ :trackable, :validatable, :lockable, :confirmable, :timeoutable, :password_expirable
55
+
56
+ #================================
57
+ # Validation
58
+ #================================
59
+ validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
60
+
61
+ if Devise.password_regex
62
+ validates :password, format: { with: Devise.password_regex, message: Devise.password_regex_message }, if: :password_required?
63
+ end
64
+
65
+ #================================
66
+ # Associations
67
+ #================================
68
+ has_many :organizations, class_name: 'MnoEnterprise::Organization'
69
+ has_many :org_invites, class_name: 'MnoEnterprise::OrgInvite'
70
+ has_one :deletion_request, class_name: 'MnoEnterprise::DeletionRequest'
71
+ has_many :dashboards, class_name: 'MnoEnterprise::Impac::Dashboard'
72
+ has_many :teams, class_name: 'MnoEnterprise::Team'
73
+
74
+ #================================
75
+ # Callbacks
76
+ #================================
77
+ before_save :expire_user_cache
78
+
79
+ #================================
80
+ # Class Methods
81
+ #================================
82
+ # The auth_hash includes an email and password
83
+ # Return nil in case of failure
84
+ def self.authenticate(auth_hash)
85
+ u = self.post("user_sessions", auth_hash)
86
+
87
+ if u && u.id
88
+ u.clear_attribute_changes!
89
+ return u
90
+ end
91
+
92
+ nil
93
+ end
94
+
95
+ #================================
96
+ # Devise Confirmation
97
+ # TODO: should go in a module
98
+ #================================
99
+
100
+
101
+ # Override Devise to allow confirmation via original token
102
+ # Less secure but useful if user has been created by Maestrano Enterprise
103
+ # (happens when an orga_invite is sent to a new user)
104
+ #
105
+ # Find a user by its confirmation token and try to confirm it.
106
+ # If no user is found, returns a new user with an error.
107
+ # If the user is already confirmed, create an error for the user
108
+ # Options must have the confirmation_token
109
+ def self.confirm_by_token(confirmation_token)
110
+ confirmable = self.find_for_confirmation(confirmation_token)
111
+ confirmable.perform_confirmation(confirmation_token)
112
+ confirmable
113
+ end
114
+
115
+ # Find a user using a confirmation token
116
+ def self.find_for_confirmation(confirmation_token)
117
+ original_token = confirmation_token
118
+ confirmation_token = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
119
+
120
+ confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
121
+ confirmable = find_or_initialize_with_error_by(:confirmation_token, original_token) if confirmable.errors.any?
122
+ confirmable
123
+ end
124
+
125
+ # Confirm the user and store confirmation_token
126
+ def perform_confirmation(confirmation_token)
127
+ self.confirm if self.persisted?
128
+ self.confirmation_token = confirmation_token
129
+ end
130
+
131
+ # It may happen that that the errors attribute become nil, which breaks the controller logic (rails responder)
132
+ # This getter ensures that 'errors' is always initialized
133
+ def errors
134
+ @errors ||= ActiveModel::Errors.new(self)
135
+ end
136
+
137
+ #================================
138
+ # Instance Methods
139
+ #================================
140
+
141
+ def to_s
142
+ "#{name} #{surname}"
143
+ end
144
+
145
+ # Format for audit log
146
+ def to_audit_event
147
+ {
148
+ user_name: to_s,
149
+ user_email: email
150
+ }
151
+ end
152
+
153
+ # Default value for failed attempts
154
+ def failed_attempts
155
+ read_attribute(:failed_attempts) || 0
156
+ end
157
+
158
+ # Override Devise default method
159
+ def authenticatable_salt
160
+ read_attribute(:authenticatable_salt)
161
+ end
162
+
163
+ # Return the role of this user for the provided
164
+ # organization
165
+ def role(organization = nil)
166
+ # Return cached version if available
167
+ return self.read_attribute(:role) if !organization
168
+
169
+ org = self.organizations.to_a.find { |o| o.id.to_s == organization.id.to_s }
170
+ org ? org.role : nil
171
+ end
172
+
173
+ def expire_user_cache
174
+ Rails.cache.delete(['user', self.to_key])
175
+ true # Don't skip save if above return false (memory_store)
176
+ end
177
+
178
+ def refresh_user_cache
179
+ self.reload
180
+ Rails.cache.write(['user', self.to_key], self)
181
+ end
182
+ end
183
+ end