mno-enterprise-api 3.3.2 → 3.3.3

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/mno_enterprise/config.js.coffee.erb +3 -1
  3. data/app/assets/javascripts/mno_enterprise/error_page.js +60 -0
  4. data/app/controllers/devise/password_expired_controller.rb +3 -7
  5. data/app/controllers/mno_enterprise/jpi/v1/admin/invites_controller.rb +7 -17
  6. data/app/controllers/mno_enterprise/jpi/v1/admin/organizations_controller.rb +8 -3
  7. data/app/controllers/mno_enterprise/jpi/v1/admin/tenant_invoices_controller.rb +1 -1
  8. data/app/controllers/mno_enterprise/jpi/v1/admin/users_controller.rb +1 -1
  9. data/app/controllers/mno_enterprise/jpi/v1/app_instances_sync_controller.rb +2 -0
  10. data/app/controllers/mno_enterprise/status_controller.rb +4 -0
  11. data/app/views/devise/password_expired/show.html.haml +21 -19
  12. data/app/views/mno_enterprise/application/error_page.html.haml +20 -0
  13. data/app/views/mno_enterprise/auth/confirmations/_form.html.haml +1 -1
  14. data/app/views/mno_enterprise/auth/sessions/new.html.haml +3 -0
  15. data/app/views/mno_enterprise/jpi/v1/admin/base_resource/_member.json.jbuilder +13 -6
  16. data/app/views/mno_enterprise/jpi/v1/admin/organizations/index.json.jbuilder +0 -1
  17. data/app/views/mno_enterprise/jpi/v1/current_users/show.json.jbuilder +2 -1
  18. data/config/initializers/devise.rb +7 -6
  19. data/config/initializers/devise_patch.rb +39 -0
  20. data/config/initializers/health_check.rb +8 -0
  21. data/config/routes.rb +5 -1
  22. data/lib/mno_enterprise/api/engine.rb +1 -1
  23. data/lib/mno_enterprise/concerns/controllers/jpi/v1/current_users_controller.rb +12 -1
  24. data/lib/mno_enterprise/concerns/controllers/jpi/v1/organizations_controller.rb +2 -1
  25. data/lib/mno_enterprise/concerns/controllers/pages_controller.rb +5 -0
  26. data/lib/mno_enterprise/concerns/mailers/system_notification_mailer.rb +5 -1
  27. data/spec/controllers/mno_enterprise/jpi/v1/admin/invites_controller_spec.rb +4 -3
  28. data/spec/controllers/mno_enterprise/jpi/v1/admin/organizations_controller_spec.rb +3 -1
  29. data/spec/controllers/mno_enterprise/jpi/v1/current_users_controller_spec.rb +20 -1
  30. data/spec/controllers/mno_enterprise/jpi/v1/organizations_controller_spec.rb +5 -1
  31. data/spec/controllers/mno_enterprise/pages_controller_spec.rb +17 -1
  32. data/spec/controllers/mno_enterprise/status_controller_spec.rb +0 -1
  33. data/spec/mailer/mno_enterprise/system_notification_mailer_spec.rb +23 -8
  34. data/spec/requests/devise/authentication_spec.rb +1 -1
  35. data/spec/requests/mno_enterprise/healthcheck_spec.rb +58 -0
  36. data/spec/requests/mno_enterprise/status_spec.rb +62 -0
  37. data/spec/routing/mno_enterprise/pages_controller_routing_spec.rb +4 -0
  38. metadata +16 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 846a1b37e09b4377d95942c2a767e6940b6accf9
4
- data.tar.gz: 523280be7a240646691ccacb1b9da059154ac75c
3
+ metadata.gz: '099fadd5ea1001d81625619146fae278ac0e4dc5'
4
+ data.tar.gz: 6ae4bbbc3816b0586207eaa69ef00553d15757e4
5
5
  SHA512:
6
- metadata.gz: 6f52fd948f00c51ea0d3b450864ee3ca443fc3f57f82c45b93994b5be7a1ee9e91fdfc0e91c93aa589225f156a26bb28b3037707f10a279a4a8db79b273cabd7
7
- data.tar.gz: 57243387693928174a98c694054fbfdbd4e788101656800674a4297b4556c3a9cfd4ede660bd0d5323c65b0b0968cefb95d68f5ec338392dea89cde4b4f1c8e0
6
+ metadata.gz: c1e148479e13a8cf64bde6d1b616a7cb603f5a033f938f410bce5dcfa69d2e2ea58881437ee395568ccaede17d8cc1e56a8c03485e8808acad167e3bc63f766e
7
+ data.tar.gz: 6f10048ccefcb4eff756ee4cc67925e0114c31c896c7729220e31238c32f6eecce8d9e1c61ddb61cdba80ce1e2441a4616af6d072336e4ed0aec5b5a7423efdb
@@ -24,4 +24,6 @@ angular.module('mnoEnterprise.configuration', [])
24
24
  .constant('INTERCOM_ID', <%= MnoEnterprise.intercom_app_id.to_json %>)
25
25
  .constant('APP_NAME', <%= MnoEnterprise.app_name.to_json %>)
26
26
  .constant('URL_CONFIG', <%= Hash(Settings.url_config).to_json %>)
27
- .constant('DEVISE_CONFIG', <%= Hash(Settings.devise).to_json %>)
27
+ .constant('DEVISE_CONFIG', <%=
28
+ Hash(Settings.devise).merge(min_password_length: Devise.password_length.min, timeout_in: Devise.timeout_in).to_json
29
+ %>)
@@ -0,0 +1,60 @@
1
+ var mnoHub = {};
2
+
3
+ mnoHub.check = function() {
4
+ var xhr = new XMLHttpRequest();
5
+ mnoHub.notify("Checking...");
6
+
7
+ xhr.onreadystatechange = function() {
8
+ if (xhr.readyState == XMLHttpRequest.DONE ) {
9
+ console.log (xhr.status);
10
+ if(xhr.status < 500) {
11
+ mnoHub.notify("Application is now running! Redirecting...")
12
+ mnoHub.stopAutoCheck();
13
+ return window.setTimeout(function() {
14
+ return mnoHub.redirect();
15
+ }, 4 * 1000);
16
+ }
17
+ window.setTimeout(function() {
18
+ mnoHub.notify('');
19
+ }, 1 * 1000);
20
+ }
21
+ }
22
+
23
+ xhr.ontimeout = function () {
24
+ mnoHub.notify('');
25
+ }
26
+
27
+ xhr.timeout = 15000; //15 seconds
28
+ xhr.open("GET", "/mnoe/health_check/full.json", true);
29
+ xhr.send();
30
+ };
31
+
32
+ mnoHub.redirect = function() {
33
+ return window.location.href = "/";
34
+ };
35
+
36
+ mnoHub.startAutoCheck = function() {
37
+ // For 500 error, we should not keep auto refreshing the page till bug
38
+ // is resolved manually by our team.
39
+ var page_error_code = document.getElementById('status_code').value;
40
+ if(parseInt(page_error_code) == 500) {
41
+ return;
42
+ }
43
+
44
+ return mnoHub.timerId = window.setInterval(function() {
45
+ return mnoHub.check();
46
+ }, 10 * 1000);
47
+ };
48
+
49
+ mnoHub.stopAutoCheck = function() {
50
+ if (mnoHub.timerId != null) {
51
+ return window.clearInterval(mnoHub.timerId);
52
+ }
53
+ };
54
+
55
+ mnoHub.notify = function(msg) {
56
+ var elem = document.getElementById('error-loader');
57
+ elem.innerHTML = msg;
58
+ }
59
+
60
+ mnoHub.startAutoCheck();
@@ -4,13 +4,9 @@ class Devise::PasswordExpiredController < DeviseController
4
4
  prepend_before_filter :authenticate_scope!, :only => [:show, :update]
5
5
 
6
6
  def show
7
- if defined?(MnoEnterprise::Frontend)
8
- respond_with(resource)
9
- else
10
- respond_with resource do |format|
11
- format.html
12
- format.json {render json: {error: 'Your password is expired. Please renew your password.'}, status: :forbidden}
13
- end
7
+ respond_with resource do |format|
8
+ format.html
9
+ format.json {render json: {error: 'Your password is expired. Please renew your password.'}, status: :forbidden}
14
10
  end
15
11
  end
16
12
 
@@ -6,13 +6,9 @@ module MnoEnterprise::Jpi::V1::Admin
6
6
  user = MnoEnterprise::User.find(params[:user_id])
7
7
  return render json: {error: 'Could not find account or user'}, status: :not_found unless @organization && user
8
8
 
9
- if user.confirmation_required?
10
- user.resend_confirmation_instructions
11
- else
12
- invite = find_org_invite(@organization, user)
13
- return render json: {error: 'No active invitation found'}, status: :not_found unless invite
14
- send_org_invite(invite)
15
- end
9
+ invite = find_org_invite(@organization, user)
10
+ return render json: {error: 'No active invitation found'}, status: :not_found unless invite
11
+ send_org_invite(user, invite)
16
12
 
17
13
  MnoEnterprise::EventLogger.info('user_invite', current_user.id, 'User invited', user, {user_email: user.email, account_name: @organization.name})
18
14
 
@@ -23,27 +19,21 @@ module MnoEnterprise::Jpi::V1::Admin
23
19
 
24
20
  private
25
21
 
26
- # Invite for unconfirmed users are automatically accepted
27
22
  def find_org_invite(organization, user)
28
- if user.confirmed?
29
- status_scope = { 'status.in' => %w(staged pending) }
30
- else
31
- status_scope = { status: 'accepted' }
32
- end
23
+ status_scope = { 'status.in' => %w(staged pending accepted) }
33
24
  organization.org_invites.where(status_scope.merge(user_id: user.id)).first
34
25
  end
35
26
 
36
27
  # Send the org invite and update the status
37
- def send_org_invite(invite)
38
- user = invite.user
28
+ def send_org_invite(user, invite)
39
29
  # Generate token if not generated
40
30
  user.send(:generate_confirmation_token!) if !user.confirmed? && user.confirmation_token.blank?
41
31
 
42
32
  MnoEnterprise::SystemNotificationMailer.organization_invite(invite).deliver_later
43
33
 
44
34
  # Update staged invite status
45
- return unless invite.status == 'staged'
46
- invite.status = 'pending'
35
+ invite.status = 'pending' if invite.status == 'staged'
36
+ invite.notification_sent_at = Time.now unless invite.notification_sent_at.present?
47
37
  invite.save
48
38
  end
49
39
  end
@@ -6,7 +6,7 @@ module MnoEnterprise
6
6
  if params[:terms]
7
7
  # Search mode
8
8
  @organizations = []
9
- JSON.parse(params[:terms]).map { |t| @organizations = @organizations | MnoEnterprise::Organization.where(Hash[*t]) }
9
+ JSON.parse(params[:terms]).map { |t| @organizations = @organizations | MnoEnterprise::Organization.where(Hash[*t]).fetch }
10
10
  response.headers['X-Total-Count'] = @organizations.count
11
11
  else
12
12
  # Index mode
@@ -28,7 +28,7 @@ module MnoEnterprise
28
28
 
29
29
  # GET /mnoe/jpi/v1/admin/organizations/in_arrears
30
30
  def in_arrears
31
- @arrears = MnoEnterprise::ArrearsSituation.all
31
+ @arrears = MnoEnterprise::ArrearsSituation.all.fetch
32
32
  end
33
33
 
34
34
  # GET /mnoe/jpi/v1/admin/organizations/count
@@ -80,7 +80,12 @@ module MnoEnterprise
80
80
  status: 'staged' # Will be updated to 'accepted' for unconfirmed users
81
81
  )
82
82
 
83
- @user = user.confirmed? ? invite : user.reload
83
+ @user = if user.confirmed?
84
+ invite.accept!(user)
85
+ invite.reload
86
+ else
87
+ user.reload
88
+ end
84
89
  end
85
90
 
86
91
  protected
@@ -3,7 +3,7 @@ module MnoEnterprise
3
3
 
4
4
  # GET /mnoe/jpi/v1/admin/tenant_invoices
5
5
  def index
6
- @tenant_invoices = MnoEnterprise::TenantInvoice.all
6
+ @tenant_invoices = MnoEnterprise::TenantInvoice.all.fetch
7
7
  end
8
8
 
9
9
  # GET /mnoe/jpi/v1/admin/tenant_invoices/1
@@ -6,7 +6,7 @@ module MnoEnterprise
6
6
  if params[:terms]
7
7
  # Search mode
8
8
  @users = []
9
- JSON.parse(params[:terms]).map { |t| @users = @users | MnoEnterprise::User.where(Hash[*t]) }
9
+ JSON.parse(params[:terms]).map { |t| @users = @users | MnoEnterprise::User.where(Hash[*t]).fetch }
10
10
  response.headers['X-Total-Count'] = @users.count
11
11
  else
12
12
  # Index mode
@@ -2,6 +2,8 @@ module MnoEnterprise
2
2
  class Jpi::V1::AppInstancesSyncController < Jpi::V1::BaseResourceController
3
3
  CONNECTOR_STATUS_RUNNING = ['PENDING', 'RUNNING']
4
4
 
5
+ prepend_before_action :skip_devise_trackable, only: :index
6
+
5
7
  # GET /mnoe/jpi/v1/organization/org-fbba/app_instances_sync
6
8
  def index
7
9
  authorize! :check_apps_sync, @parent_organization
@@ -1,6 +1,10 @@
1
1
  # Health Check endpoint
2
2
  module MnoEnterprise
3
3
  class StatusController < ApplicationController
4
+ # Skip filters than rely on MnoHub (RemoteAuthenticatable)
5
+ skip_before_filter :handle_password_change
6
+ skip_before_filter :perform_return_to
7
+
4
8
  # Simple check to see that the app is up
5
9
  # Returns:
6
10
  # {status: 'Ok'}
@@ -3,30 +3,32 @@
3
3
  .row
4
4
  .login-box-wrapper
5
5
  .login-box-title
6
- %h2 You password has expired. Please renew it.
6
+ %h2 Password Expired
7
7
  .login-box
8
8
  .brand-logo
9
9
 
10
- = form_for(resource, as: resource_name, url: mno_enterprise.user_password_expired_path, :html => { method: :put, class: 'form-horizontal' }) do |f|
11
- = devise_error_messages!
10
+ = form_for(resource, as: resource_name, url: mno_enterprise.user_password_expired_path, :html => { method: :put, class: 'form-horizontal' }) do |f|
11
+ = devise_error_messages!
12
12
 
13
- .row
14
- .col-sm-6
15
- = f.label :current_password, "Current password"
16
- = f.password_field :current_password, :class => 'form-control'
13
+ .row
14
+ .col-sm-12
15
+ = f.password_field :current_password, placeholder: "*Current password", required: true, :class => 'form-control'
17
16
 
18
- .row
19
- .col-sm-6
20
- = f.label :password, "New password"
21
- = f.password_field :password, :class => 'form-control'
17
+ %br
22
18
 
23
- .col-sm-6
24
- = f.label :password_confirmation, "Confirm new password"
25
- = f.password_field :password_confirmation, :class => 'form-control'
19
+ .row
20
+ .col-sm-12
21
+ = f.password_field :password, placeholder: "*New password", required: true, autocomplete: "off", :class => 'form-control'
26
22
 
27
- %br
23
+ %br
28
24
 
29
- .row
30
- .col-sm-12
31
- .text-center
32
- = f.submit "Change my password", class: "btn btn-warning"
25
+ .row
26
+ .col-sm-12
27
+ = f.password_field :password_confirmation, placeholder: "*Confirm new password", required: true, autocomplete: "off", :class => 'form-control'
28
+
29
+ %br
30
+
31
+ .row
32
+ .col-sm-12
33
+ .text-center
34
+ = f.submit "Change my password", class: "btn btn-warning"
@@ -0,0 +1,20 @@
1
+ .error-page
2
+ .container
3
+ .error-code
4
+ = @status
5
+ .error-title
6
+ = t("mno_enterprise.errors.#{@status}.title")
7
+ %hr
8
+ .error-context
9
+ %p
10
+ = t("mno_enterprise.errors.#{@status}.description", support_email: MnoEnterprise.support_email)
11
+ .error-button
12
+ %button
13
+ = link_to(mno_enterprise.root_path) do
14
+ %i{class: "fa fa-home"}
15
+ = t("mno_enterprise.errors.redirection")
16
+ #error-loader
17
+
18
+ = hidden_field_tag :status_code, @status
19
+
20
+ = javascript_include_tag "mno_enterprise/error_page"
@@ -30,7 +30,7 @@
30
30
 
31
31
  - else
32
32
  .input-group
33
- = f.password_field :password, :placeholder => "*Choose Password", 'ng-model' => 'user.password', class: 'form-control', 'ng-minlength' => 6, 'mno-password-strength' => true, "password-score" => 'user.$pwdScore', :required => true
33
+ = f.password_field :password, :placeholder => '*Choose Password', :class => 'form-control', :required => true, 'ng-model' => 'user.password', 'ng-minlength' => Devise.password_length.min, 'mno-password-strength' => true, 'password-score' => 'user.$pwdScore'
34
34
  %span.input-group-addon.classic.pw-strength-indicator{'ng-class' => 'user.$pwdScore.class' }
35
35
  {{ user.$pwdScore.value }}
36
36
 
@@ -1,6 +1,9 @@
1
1
  .registration
2
2
  .container
3
3
  .row
4
+ - if params[:session_timeout].present?
5
+ .alert.alert-warning.text-center
6
+ = t('mno_enterprise.auth.sessions.new.timeout')
4
7
  .login-box-wrapper
5
8
  .login-box-title
6
9
  %h2= t('mno_enterprise.auth.sessions.new.title')
@@ -3,27 +3,34 @@ if member.is_a?(MnoEnterprise::User)
3
3
  json.entity 'User'
4
4
  json.role member.role(organization) if organization
5
5
  json.admin_role member.admin_role
6
+ invite = MnoEnterprise::OrgInvite.find_by(user_id: member.id, organization_id: organization.id)
6
7
 
7
8
  status = case
8
- when member.confirmed? then 'active'
9
+ when member.confirmed?
10
+ invite.blank? || invite.notification_sent_at.present? ? 'active' : 'notify'
9
11
  when member.confirmation_sent_at.nil? then 'pending'
10
- when member.confirmation_sent_at.present? then 'invited'
12
+ when !member.confirmed? then 'invited'
11
13
  end
12
14
 
13
15
  user = member
14
-
15
16
  elsif member.is_a?(MnoEnterprise::OrgInvite)
16
17
  json.entity 'OrgInvite'
17
18
  json.role member.user_role
19
+ user = member.user
20
+ invite = member
18
21
 
19
22
  status = case member.status
20
23
  when 'staged' then 'pending'
21
- when 'pending' then 'invited'
22
- when 'accepted' then 'active'
24
+ when 'pending'
25
+ 'invited'
26
+ when 'accepted'
27
+ invite.notification_sent_at.present? ? 'active' : 'notify'
23
28
  end
24
29
 
25
- user = member.user
26
30
  end
27
31
 
32
+ allow_impersonation = invite.present? ? invite.status == 'accepted' : true
33
+
28
34
  json.extract! user, :id, :created_at, :email, :name, :surname
29
35
  json.status status
36
+ json.allow_impersonation allow_impersonation
@@ -1,7 +1,6 @@
1
1
  json.organizations do
2
2
  json.array! @organizations do |organization|
3
3
  json.partial! 'organization', organization: organization
4
- json.partial! 'credit_card', credit_card: organization.credit_card
5
4
  end
6
5
  end
7
6
  json.metadata @organizations.metadata if @organizations.respond_to?(:metadata)
@@ -1,4 +1,4 @@
1
- json.cache! ['v1', @user.cache_key] do
1
+ json.cache! ['v1', @user.cache_key, session[:impersonator_user_id]] do
2
2
  json.current_user do
3
3
  json.id @user.id
4
4
  json.name @user.name
@@ -16,6 +16,7 @@ json.cache! ['v1', @user.cache_key] do
16
16
  json.sso_session @user.sso_session
17
17
  json.admin_role @user.admin_role
18
18
  json.avatar_url avatar_url(@user)
19
+ json.tos_accepted_at @user.meta_data[:tos_accepted_at] || false
19
20
  if current_impersonator
20
21
  json.current_impersonator true
21
22
  json.current_impersonator_role current_impersonator.admin_role
@@ -163,7 +163,7 @@ Devise.setup do |config|
163
163
  # Defines which strategy will be used to lock an account.
164
164
  # :failed_attempts = Locks an account after a number of failed attempts to sign in.
165
165
  # :none = No lock strategy. You should handle locking by yourself.
166
- # config.lock_strategy = :failed_attempts
166
+ config.lock_strategy = :failed_attempts
167
167
 
168
168
  # Defines which key will be used when locking and unlocking an account
169
169
  # config.unlock_keys = [ :email ]
@@ -173,17 +173,17 @@ Devise.setup do |config|
173
173
  # :time = Re-enables login after a certain amount of time (see :unlock_in below)
174
174
  # :both = Enables both strategies
175
175
  # :none = No unlock strategy. You should handle unlocking by yourself.
176
- # config.unlock_strategy = :both
176
+ config.unlock_strategy = :both
177
177
 
178
178
  # Number of authentication tries before locking an account if lock_strategy
179
179
  # is failed attempts.
180
- # config.maximum_attempts = 20
180
+ config.maximum_attempts = 5
181
181
 
182
182
  # Time interval to unlock the account if :time is enabled as unlock_strategy.
183
- # config.unlock_in = 1.hour
183
+ config.unlock_in = 1.hour
184
184
 
185
185
  # Warn on the last attempt before the account is locked.
186
- # config.last_attempt_warning = true
186
+ config.last_attempt_warning = true
187
187
 
188
188
  # ==> Configuration for :recoverable
189
189
  #
@@ -254,7 +254,8 @@ Devise.setup do |config|
254
254
  end
255
255
  if ENV['OAUTH_FACEBOOK_KEY'] && ENV['OAUTH_FACEBOOK_SECRET']
256
256
  require 'omniauth-facebook'
257
- config.omniauth :facebook, ENV['OAUTH_FACEBOOK_KEY'], ENV['OAUTH_FACEBOOK_SECRET'], secure_image_url: true
257
+ config.omniauth :facebook, ENV['OAUTH_FACEBOOK_KEY'], ENV['OAUTH_FACEBOOK_SECRET'],
258
+ secure_image_url: true, info_fields: 'name,email,first_name,last_name'
258
259
  end
259
260
 
260
261
  # ==> Warden configuration
@@ -0,0 +1,39 @@
1
+ require "action_controller/metal"
2
+
3
+ module Devise
4
+ # Failure application that will be called every time :warden is thrown from
5
+ # any strategy or hook. Responsible for redirect the user to the sign in
6
+ # page based on current scope and mapping. If no scope is given, redirect
7
+ # to the default_url.
8
+ class FailureApp < ActionController::Metal
9
+
10
+ protected
11
+
12
+ # Monkey patching to unset opts[:script_name]
13
+ # See https://github.com/plataformatec/devise/issues/4127
14
+ def scope_url
15
+ opts = {}
16
+ route = route(scope)
17
+ opts[:format] = request_format unless skip_format?
18
+
19
+ config = Rails.application.config
20
+
21
+ # Monkey Patch
22
+ if config.respond_to?(:relative_url_root) && config.relative_url_root.present?
23
+ opts[:script_name] = config.relative_url_root
24
+ end
25
+ # EO Monkey Patch
26
+
27
+ router_name = Devise.mappings[scope].router_name || Devise.available_router_name
28
+ context = send(router_name)
29
+
30
+ if context.respond_to?(route)
31
+ context.send(route, opts)
32
+ elsif respond_to?(:root_url)
33
+ root_url(opts)
34
+ else
35
+ "/"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -31,3 +31,11 @@ HealthCheck.setup do |config|
31
31
  MnoEnterprise::HealthCheck.perform_mno_hub_check
32
32
  end
33
33
  end
34
+
35
+ # Monkey patch HealthCheckController to skip filters than rely on MnoHub (RemoteAuthenticatable)
36
+ module HealthCheck
37
+ class HealthCheckController
38
+ skip_before_filter :handle_password_change
39
+ skip_before_filter :perform_return_to
40
+ end
41
+ end
@@ -10,7 +10,10 @@ MnoEnterprise::Engine.routes.draw do
10
10
  # Health Status
11
11
  get '/ping', to: 'status#ping'
12
12
  get '/version', to: 'status#version'
13
- get 'health_check(/:checks)(.:format)', to: '/health_check/health_check#index'
13
+ get '/health_check(/:checks)(.:format)', to: '/health_check/health_check#index'
14
+
15
+ # Error Handling
16
+ get '/errors/:error_code', to: 'pages#error'
14
17
 
15
18
  # App Provisioning
16
19
  resources :provision, only: [:new, :create]
@@ -110,6 +113,7 @@ MnoEnterprise::Engine.routes.draw do
110
113
  resource :current_user, only: [:show, :update] do
111
114
  put :update_password
112
115
  put :register_developer
116
+ put :update_tos
113
117
  #post :deletion_request, action: :create_deletion_request
114
118
  #delete :deletion_request, action: :cancel_deletion_request
115
119
  end
@@ -7,7 +7,7 @@ module MnoEnterprise
7
7
 
8
8
  # Add assets
9
9
  if config.respond_to? (:assets)
10
- config.assets.precompile += %w( mno_enterprise/config.js )
10
+ config.assets.precompile += %w( mno_enterprise/config.js mno_enterprise/error_page.js )
11
11
 
12
12
  # Allow sprockets to find file in the config/ path
13
13
  config.before_configuration do
@@ -7,7 +7,7 @@ module MnoEnterprise::Concerns::Controllers::Jpi::V1::CurrentUsersController
7
7
  # 'included do' causes the included code to be evaluated in the
8
8
  # context where it is included rather than being executed in the module's context
9
9
  included do
10
- before_filter :authenticate_user!, only: [:update, :update_password]
10
+ before_filter :authenticate_user!, only: [:update, :update_password, :update_tos]
11
11
  before_filter :user_management_enabled?, only: [:update, :update_password]
12
12
  respond_to :json
13
13
  end
@@ -58,6 +58,17 @@ module MnoEnterprise::Concerns::Controllers::Jpi::V1::CurrentUsersController
58
58
  end
59
59
  end
60
60
 
61
+ def update_tos
62
+ @user = current_user
63
+
64
+ if @user.update({ meta_data: { tos_accepted_at: Time.now } })
65
+ MnoEnterprise::EventLogger.info('user_update_tos_accepted_at', current_user.id, 'User accepted TOS', @user)
66
+ render :show
67
+ else
68
+ render json: @user.errors, status: :bad_request
69
+ end
70
+ end
71
+
61
72
  private
62
73
  def user_params
63
74
  params.require(:user).permit(:name, :surname, :email, :company, :settings, :phone, :website, :phone_country_code, :current_password, :password, :password_confirmation)
@@ -116,7 +116,8 @@ module MnoEnterprise::Concerns::Controllers::Jpi::V1::OrganizationsController
116
116
  user_email: invite['email'],
117
117
  user_role: invite['role'],
118
118
  team_id: invite['team_id'],
119
- referrer_id: current_user.id
119
+ referrer_id: current_user.id,
120
+ notification_sent_at: Time.now
120
121
  )
121
122
 
122
123
  MnoEnterprise::SystemNotificationMailer.organization_invite(@org_invite).deliver_now
@@ -74,6 +74,11 @@ module MnoEnterprise::Concerns::Controllers::PagesController
74
74
  end
75
75
  end
76
76
 
77
+ # Error page
78
+ def error
79
+ handle_mnohub_error(params[:error_code])
80
+ end
81
+
77
82
  private
78
83
  def app_instance_hash(app_instance)
79
84
  return {} unless app_instance
@@ -123,7 +123,7 @@ module MnoEnterprise::Concerns::Mailers::SystemNotificationMailer
123
123
  def organization_invite(org_invite)
124
124
  new_user = !org_invite.user.confirmed?
125
125
  confirmation_link = new_user ? user_confirmation_url(confirmation_token: org_invite.user.confirmation_token) : org_invite_url(org_invite, token: org_invite.token)
126
- email_template = new_user ? 'organization-invite-new-user' : 'organization-invite-existing-user'
126
+ email_template = new_user ? 'organization-invite-new-user' : existing_user_template(org_invite)
127
127
 
128
128
  MnoEnterprise::MailClient.deliver(email_template,
129
129
  default_sender,
@@ -194,4 +194,8 @@ module MnoEnterprise::Concerns::Mailers::SystemNotificationMailer
194
194
  invitee_email: org_invite.user.email,
195
195
  }
196
196
  end
197
+
198
+ def existing_user_template(invite)
199
+ invite.status == 'accepted' ? 'organization-invite-notification' : 'organization-invite-existing-user'
200
+ end
197
201
  end
@@ -27,7 +27,7 @@ module MnoEnterprise
27
27
  # API stubs
28
28
  before do
29
29
  api_stub_for(get: "/organizations/#{organization.id}", response: from_api(organization))
30
- api_stub_for(get: "/organizations/#{organization.id}/org_invites?filter[status.in][]=pending&filter[status.in][]=staged&filter[user_id]=#{invitee.id}", response: from_api([invite]))
30
+ api_stub_for(get: "/organizations/#{organization.id}/org_invites?filter[status.in][]=pending&filter[status.in][]=staged&filter[status.in][]=accepted&filter[user_id]=#{invitee.id}", response: from_api([invite]))
31
31
 
32
32
  allow(MnoEnterprise::User).to receive(:find) do |user_id|
33
33
  case user_id.to_i
@@ -57,9 +57,10 @@ module MnoEnterprise
57
57
  context 'new user' do
58
58
  before { invitee.confirmed_at = nil }
59
59
 
60
- it 'sends the confirmation instructions' do
61
- expect(invitee).to receive(:resend_confirmation_instructions)
60
+ it 'sends organization invite to new user' do
61
+ expect(SystemNotificationMailer).to receive(:organization_invite).with(invite).and_return(message_delivery)
62
62
  subject
63
+ expect(response).to be_success
63
64
  end
64
65
  end
65
66
  end
@@ -54,9 +54,12 @@ module MnoEnterprise
54
54
  api_stub_for(get: "/organizations/#{organization.id}/users", response: from_api([user]))
55
55
  api_stub_for(get: "/organizations/#{organization.id}/org_invites", response: from_api([org_invite]))
56
56
  api_stub_for(get: "/organizations/#{organization.id}/app_instances", response: from_api([app_instance]))
57
+ api_stub_for(get: "/org_invites?filter[organization_id]=#{organization.id}&filter[user_id]=#{user.id}&limit=1", response: from_api([org_invite]))
57
58
  api_stub_for(get: "/organizations/#{organization.id}/credit_card", response: from_api([credit_card]))
58
59
  api_stub_for(get: "/arrears_situations", response: from_api([arrears]))
60
+ api_stub_for(get: "/org_invites/#{org_invite.id}", response: from_api(org_invite))
59
61
  api_stub_for(post: "/organizations", response: from_api([organization]))
62
+ api_stub_for(put: "/org_invites/#{org_invite.id}", response: from_api([org_invite]))
60
63
  end
61
64
 
62
65
  let(:expected_hash_for_organizations) {
@@ -67,7 +70,6 @@ module MnoEnterprise
67
70
  'name' => organization.name,
68
71
  'soa_enabled' => organization.soa_enabled,
69
72
  'created_at' => organization.created_at,
70
- 'credit_card' => {'presence' => organization.credit_card?},
71
73
  'account_frozen' => organization.account_frozen
72
74
  }],
73
75
  'metadata' => {'pagination' => {'count' => 1}}
@@ -33,7 +33,8 @@ module MnoEnterprise
33
33
  'website' => res.website,
34
34
  'sso_session' => res.sso_session,
35
35
  'admin_role' => res.admin_role,
36
- 'avatar_url' => avatar_url(res)
36
+ 'avatar_url' => avatar_url(res),
37
+ 'tos_accepted_at' => res.meta_data[:tos_accepted_at] || false
37
38
  }
38
39
 
39
40
  if res.id
@@ -83,6 +84,7 @@ module MnoEnterprise
83
84
  subject { get :show }
84
85
 
85
86
  describe 'guest' do
87
+ before { expect_any_instance_of(MnoEnterprise::User).to receive(:meta_data).and_return({}) }
86
88
  it 'is successful' do
87
89
  subject
88
90
  expect(response).to be_success
@@ -140,6 +142,23 @@ module MnoEnterprise
140
142
  end
141
143
  end
142
144
 
145
+ describe 'PUT #update_tos' do
146
+ before { api_stub_for(put: "/users/#{user.id}", response: from_api(user)) }
147
+ subject { put :update_tos }
148
+
149
+ describe 'guest' do
150
+ before { subject }
151
+ it { expect(response).to_not be_success }
152
+ end
153
+
154
+ describe 'logged in' do
155
+ before { sign_in user }
156
+ before { subject }
157
+ it { expect(response).to be_success }
158
+ it { expect(controller.current_user).to eq(user) }
159
+ end
160
+ end
161
+
143
162
  describe 'PUT #update_password' do
144
163
  let(:attrs) { {current_password: 'password', password: 'blablabla', password_confirmation: 'blablabla'} }
145
164
  before { api_stub_for(put: "/users/#{user.id}", response: from_api(user)) }
@@ -291,6 +291,9 @@ module MnoEnterprise
291
291
  let(:relation) { instance_double('Her::Model::Relation') }
292
292
  before do
293
293
  allow(relation).to receive(:active).and_return(relation)
294
+
295
+ @fake_time = Time.now
296
+ Time.stub(:now) { @fake_time }
294
297
  end
295
298
 
296
299
  it 'creates an invitation' do
@@ -302,7 +305,8 @@ module MnoEnterprise
302
305
  user_email: 'newmember@maestrano.com',
303
306
  user_role: 'Power User',
304
307
  team_id: team.id.to_s,
305
- referrer_id: user.id
308
+ referrer_id: user.id,
309
+ notification_sent_at: Time.now
306
310
  ).and_return(org_invite)
307
311
  subject
308
312
  end
@@ -76,10 +76,26 @@ module MnoEnterprise
76
76
  before { subject }
77
77
 
78
78
  it { expect(response).to be_success }
79
-
79
+
80
80
  it 'returns the apps in the correct alphabetical order' do
81
81
  expect(assigns(:apps)).to eq([app2, app1, app3])
82
82
  end
83
83
  end
84
+
85
+ describe 'GET #error' do
86
+ subject { get :error, error_code: error_code, format: :json }
87
+
88
+ context '503 error' do
89
+ let(:error_code) { 503 }
90
+ it { is_expected.to have_http_status(:service_unavailable) }
91
+ it { expect(subject.body).to eq({error: 'API hub unavailable'}.to_json) }
92
+ end
93
+
94
+ context '429 error' do
95
+ let(:error_code) { 429 }
96
+ it { is_expected.to have_http_status(:too_many_requests) }
97
+ it { expect(subject.body).to eq({error: 'API hub unavailable'}.to_json) }
98
+ end
99
+ end
84
100
  end
85
101
  end
@@ -18,7 +18,6 @@ module MnoEnterprise
18
18
  it { is_expected.to respond_with(200) }
19
19
 
20
20
  it 'returns the main app version' do
21
- puts MnoEnterprise::APP_VERSION
22
21
  expect(data['app-version']).to eq(MnoEnterprise::APP_VERSION)
23
22
  end
24
23
 
@@ -125,14 +125,29 @@ module MnoEnterprise
125
125
  let(:org_invite) { build(:org_invite, user: invitee, referrer: user) }
126
126
 
127
127
  context 'when invitee is a confirmed user' do
128
- it 'sends the right email' do
129
- expect(MnoEnterprise::MailClient).to receive(:deliver).with('organization-invite-existing-user',
130
- SystemNotificationMailer::DEFAULT_SENDER,
131
- { name: "#{invitee.name} #{invitee.surname}".strip, email: invitee.email },
132
- invite_vars(org_invite).merge(confirmation_link: routes.org_invite_url(id: org_invite.id, token: org_invite.token))
133
- )
134
-
135
- subject.organization_invite(org_invite).deliver_now
128
+ context 'when invite is from Admin Panel' do
129
+ before { org_invite.status = 'accepted' }
130
+
131
+ it 'sends the right email' do
132
+ expect(MnoEnterprise::MailClient).to receive(:deliver).with('organization-invite-notification',
133
+ SystemNotificationMailer::DEFAULT_SENDER,
134
+ { name: "#{invitee.name} #{invitee.surname}".strip, email: invitee.email },
135
+ invite_vars(org_invite).merge(confirmation_link: routes.org_invite_url(id: org_invite.id, token: org_invite.token))
136
+ )
137
+
138
+ subject.organization_invite(org_invite).deliver_now
139
+ end
140
+ end
141
+ context 'when invite is from Customer View' do
142
+ it 'sends the right email' do
143
+ expect(MnoEnterprise::MailClient).to receive(:deliver).with('organization-invite-existing-user',
144
+ SystemNotificationMailer::DEFAULT_SENDER,
145
+ { name: "#{invitee.name} #{invitee.surname}".strip, email: invitee.email },
146
+ invite_vars(org_invite).merge(confirmation_link: routes.org_invite_url(id: org_invite.id, token: org_invite.token))
147
+ )
148
+
149
+ subject.organization_invite(org_invite).deliver_now
150
+ end
136
151
  end
137
152
  end
138
153
 
@@ -3,7 +3,7 @@ require 'rails_helper'
3
3
  module MnoEnterprise
4
4
  RSpec.describe "Remote Authentication", type: :request do
5
5
 
6
- let(:user) { build(:user) }
6
+ let(:user) { build(:user, password_valid: true) }
7
7
  before { api_stub_for(get: "/users/#{user.id}", response: from_api(user)) }
8
8
  before { api_stub_for(put: "/users/#{user.id}", response: from_api(user)) }
9
9
 
@@ -0,0 +1,58 @@
1
+ require 'rails_helper'
2
+
3
+ module MnoEnterprise
4
+ # Test the Status controller is still working when MnoHub is down
5
+ # The behavior is tested in the ControllerSpec
6
+ RSpec.describe "Health Check", type: :request do
7
+ include MnoEnterprise::TestingSupport::RequestSpecHelper
8
+
9
+ describe 'simple' do
10
+ subject { get '/mnoe/health_check.json' }
11
+ let(:data) { JSON.parse(response.body) }
12
+
13
+ context 'with a signed in user' do
14
+ include_context 'signed in user'
15
+
16
+ context 'when MnoHub is up' do
17
+ before { subject }
18
+
19
+ it { expect(response).to have_http_status(:ok), response.body }
20
+ it { expect(data['healthy']).to be true }
21
+ end
22
+
23
+ context 'when MnoHub is down' do
24
+ before { clear_api_stubs }
25
+ before { subject }
26
+
27
+ it { expect(response).to have_http_status(:ok), response.body }
28
+ it { expect(data['healthy']).to be true }
29
+ end
30
+ end
31
+ end
32
+
33
+ describe 'full' do
34
+ subject { get '/mnoe/health_check/full.json' }
35
+ let(:data) { JSON.parse(response.body) }
36
+
37
+ context 'with a signed in user' do
38
+ include_context 'signed in user'
39
+
40
+ context 'when MnoHub is up' do
41
+ before { api_stub_for(get: '/apps?limit=1&sort[]=id.asc', response: from_api([FactoryGirl.build(:app)])) }
42
+ before { subject }
43
+
44
+ it { expect(response).to have_http_status(:ok), response.body }
45
+ it { expect(data['healthy']).to be true }
46
+ end
47
+
48
+ context 'when MnoHub is down' do
49
+ before { clear_api_stubs }
50
+ before { subject }
51
+
52
+ it { expect(response).to have_http_status(:internal_server_error), response.body }
53
+ it { expect(data['healthy']).to be false }
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,62 @@
1
+ require 'rails_helper'
2
+
3
+ module MnoEnterprise
4
+ # Test the Status controller is still working when MnoHub is down
5
+ # The behavior is tested in the ControllerSpec
6
+ RSpec.describe "Status", type: :request do
7
+ include MnoEnterprise::TestingSupport::RequestSpecHelper
8
+
9
+ describe 'version' do
10
+ subject { get '/mnoe/version' }
11
+ let(:data) { JSON.parse(response.body) }
12
+
13
+ context 'with a signed in user' do
14
+ include_context 'signed in user'
15
+
16
+ context 'when MnoHub is up' do
17
+ before { subject }
18
+
19
+ it { expect(response).to have_http_status(:ok) }
20
+ end
21
+
22
+ context 'when MnoHub is down' do
23
+ before { clear_api_stubs }
24
+ before { subject }
25
+
26
+ it { expect(response).to have_http_status(:ok) }
27
+ end
28
+ end
29
+
30
+ context 'without a signed in user' do
31
+ before { subject }
32
+ it { expect(response).to have_http_status(:ok) }
33
+ end
34
+ end
35
+
36
+ describe 'ping' do
37
+ subject { get '/mnoe/ping' }
38
+
39
+ context 'with a signed in user' do
40
+ include_context 'signed in user'
41
+
42
+ context 'when MnoHub is up' do
43
+ before { subject }
44
+
45
+ it { expect(response).to have_http_status(:ok) }
46
+ end
47
+
48
+ context 'when MnoHub is down' do
49
+ before { clear_api_stubs }
50
+ before { subject }
51
+
52
+ it { expect(response).to have_http_status(:ok) }
53
+ end
54
+ end
55
+
56
+ context 'without a signed in user' do
57
+ before { subject }
58
+ it { expect(response).to have_http_status(:ok) }
59
+ end
60
+ end
61
+ end
62
+ end
@@ -29,5 +29,9 @@ module MnoEnterprise
29
29
  it 'routes to #terms' do
30
30
  expect(get("/terms")).to route_to("mno_enterprise/pages#terms")
31
31
  end
32
+
33
+ it 'routes to #error' do
34
+ expect(get("/errors/503")).to route_to("mno_enterprise/pages#error", error_code: '503')
35
+ end
32
36
  end
33
37
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mno-enterprise-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.2
4
+ version: 3.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Lachaume
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-11-09 00:00:00.000000000 Z
12
+ date: 2018-10-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mno-enterprise-core
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - '='
19
19
  - !ruby/object:Gem::Version
20
- version: 3.3.2
20
+ version: 3.3.3
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 3.3.2
27
+ version: 3.3.3
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: jbuilder
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -171,28 +171,28 @@ dependencies:
171
171
  requirements:
172
172
  - - "~>"
173
173
  - !ruby/object:Gem::Version
174
- version: 0.2.6
174
+ version: 0.5.3
175
175
  type: :development
176
176
  prerelease: false
177
177
  version_requirements: !ruby/object:Gem::Requirement
178
178
  requirements:
179
179
  - - "~>"
180
180
  - !ruby/object:Gem::Version
181
- version: 0.2.6
181
+ version: 0.5.3
182
182
  - !ruby/object:Gem::Dependency
183
183
  name: omniauth-facebook
184
184
  requirement: !ruby/object:Gem::Requirement
185
185
  requirements:
186
186
  - - "~>"
187
187
  - !ruby/object:Gem::Version
188
- version: 2.0.1
188
+ version: 4.0.0
189
189
  type: :development
190
190
  prerelease: false
191
191
  version_requirements: !ruby/object:Gem::Requirement
192
192
  requirements:
193
193
  - - "~>"
194
194
  - !ruby/object:Gem::Version
195
- version: 2.0.1
195
+ version: 4.0.0
196
196
  description: Maestrano Enterprise - essentials API
197
197
  email:
198
198
  - developers@maestrano.com
@@ -205,6 +205,7 @@ files:
205
205
  - app/assets/javascripts/mno_enterprise/angular/loading-page.app.js.coffee
206
206
  - app/assets/javascripts/mno_enterprise/application.js
207
207
  - app/assets/javascripts/mno_enterprise/config.js.coffee.erb
208
+ - app/assets/javascripts/mno_enterprise/error_page.js
208
209
  - app/controllers/devise/password_expired_controller.rb
209
210
  - app/controllers/mno_enterprise/admin/invoices_controller.rb
210
211
  - app/controllers/mno_enterprise/auth/confirmations_controller.rb
@@ -262,6 +263,7 @@ files:
262
263
  - app/mailers/mno_enterprise/system_notification_mailer.rb
263
264
  - app/models/mno_enterprise/health_check.rb
264
265
  - app/views/devise/password_expired/show.html.haml
266
+ - app/views/mno_enterprise/application/error_page.html.haml
265
267
  - app/views/mno_enterprise/auth/confirmations/_form.html.haml
266
268
  - app/views/mno_enterprise/auth/confirmations/lounge.html.haml
267
269
  - app/views/mno_enterprise/auth/confirmations/new.html.haml
@@ -395,6 +397,7 @@ files:
395
397
  - config/initializers/devise.rb
396
398
  - config/initializers/devise_extension.rb
397
399
  - config/initializers/devise_log.rb
400
+ - config/initializers/devise_patch.rb
398
401
  - config/initializers/health_check.rb
399
402
  - config/initializers/main_app_version.rb
400
403
  - config/initializers/sprockets.rb
@@ -476,6 +479,8 @@ files:
476
479
  - spec/rails_helper.rb
477
480
  - spec/requests/devise/authentication_spec.rb
478
481
  - spec/requests/devise/registration_spec.rb
482
+ - spec/requests/mno_enterprise/healthcheck_spec.rb
483
+ - spec/requests/mno_enterprise/status_spec.rb
479
484
  - spec/routing/devise/confirmation_routing_spec.rb
480
485
  - spec/routing/devise/passwords_routing_spec.rb
481
486
  - spec/routing/devise/registrations_routing_spec.rb
@@ -538,7 +543,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
538
543
  version: '0'
539
544
  requirements: []
540
545
  rubyforge_project:
541
- rubygems_version: 2.6.8
546
+ rubygems_version: 2.6.14
542
547
  signing_key:
543
548
  specification_version: 4
544
549
  summary: Maestrano Enterprise - API
@@ -585,6 +590,8 @@ test_files:
585
590
  - spec/routing/devise/sessions_routing_spec.rb
586
591
  - spec/routing/devise/passwords_routing_spec.rb
587
592
  - spec/spec_helper.rb
593
+ - spec/requests/mno_enterprise/status_spec.rb
594
+ - spec/requests/mno_enterprise/healthcheck_spec.rb
588
595
  - spec/requests/devise/authentication_spec.rb
589
596
  - spec/requests/devise/registration_spec.rb
590
597
  - spec/mailer/mno_enterprise/system_notification_mailer_spec.rb