mno-enterprise-api 3.3.2 → 3.3.3

Sign up to get free protection for your applications and to get access to all the features.
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