mno-enterprise-core 3.1.4 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/images/mno_enterprise/logo-intuit.png +0 -0
- data/app/assets/images/mno_enterprise/main-logo.png +0 -0
- data/app/assets/stylesheets/mno_enterprise/mail.css +1 -4
- data/app/controllers/mno_enterprise/application_controller.rb +1 -1
- data/app/helpers/mno_enterprise/application_helper.rb +7 -7
- data/app/models/mno_enterprise/app.rb +12 -3
- data/app/models/mno_enterprise/app_answer.rb +13 -0
- data/app/models/mno_enterprise/app_comment.rb +10 -0
- data/app/models/mno_enterprise/app_feedback.rb +7 -0
- data/app/models/mno_enterprise/app_question.rb +9 -0
- data/app/models/mno_enterprise/app_review.rb +7 -0
- data/app/models/mno_enterprise/base_resource.rb +6 -1
- data/app/models/mno_enterprise/identity.rb +24 -0
- data/app/models/mno_enterprise/impac/alert.rb +10 -0
- data/app/models/mno_enterprise/impac/dashboard.rb +10 -11
- data/app/models/mno_enterprise/impac/kpi.rb +4 -2
- data/app/models/mno_enterprise/impac/widget.rb +4 -1
- data/app/models/mno_enterprise/team.rb +1 -33
- data/app/models/mno_enterprise/user.rb +109 -28
- data/app/views/system_notifications/email-change.html.erb +27 -0
- data/app/views/system_notifications/email-change.text.erb +10 -0
- data/app/views/system_notifications/password-change.html.erb +25 -0
- data/app/views/system_notifications/password-change.text.erb +7 -0
- data/app/views/system_notifications/reconfirmation-instructions.html.erb +7 -5
- data/app/views/system_notifications/reconfirmation-instructions.text.erb +3 -2
- data/config/initializers/config.rb +5 -0
- data/config/locales/models/user/en.yml +5 -0
- data/config/locales/templates/components/en.yml +9 -0
- data/config/locales/templates/dashboard/en.yml +14 -0
- data/config/locales/templates/dashboard/marketplace/en.yml +111 -8
- data/config/locales/templates/dashboard/organization/en.yml +1 -0
- data/config/locales/templates/impac/dock/en.yml +28 -0
- data/config/locales/views/auth/shared/en.yml +1 -2
- data/config/locales/views/webhook/o_auth/providers/en.yml +11 -6
- data/lib/devise/hooks/lockable.rb +13 -0
- data/lib/devise/models/remote_authenticatable.rb +31 -5
- data/lib/generators/mno_enterprise/install/install_generator.rb +7 -0
- data/lib/generators/mno_enterprise/install/templates/Procfile +1 -1
- data/lib/generators/mno_enterprise/install/templates/Procfile.dev +1 -1
- data/lib/generators/mno_enterprise/install/templates/config/application.yml +17 -0
- data/lib/generators/mno_enterprise/install/templates/config/initializers/mno_enterprise.rb +8 -3
- data/lib/generators/mno_enterprise/install/templates/config/newrelic.yml +46 -0
- data/lib/generators/mno_enterprise/install/templates/config/puma.rb +56 -0
- data/lib/generators/mno_enterprise/install/templates/config/settings/production.yml +1 -1
- data/lib/generators/mno_enterprise/install/templates/config/settings/uat.yml +1 -1
- data/lib/generators/mno_enterprise/install/templates/config/settings.yml +21 -0
- data/lib/generators/mno_enterprise/install/templates/stylesheets/variables.less +3 -0
- data/lib/her_extension/model/associations/association_proxy.rb +10 -6
- data/lib/mno_enterprise/concerns/controllers/auth/confirmations_controller.rb +1 -1
- data/lib/mno_enterprise/concerns/controllers/auth/omniauth_callbacks_controller.rb +203 -0
- data/lib/mno_enterprise/concerns/models/ability.rb +28 -0
- data/lib/mno_enterprise/concerns/models/app_instance.rb +22 -4
- data/lib/mno_enterprise/concerns/models/intercom_user.rb +28 -0
- data/lib/mno_enterprise/concerns/models/organization.rb +14 -1
- data/lib/mno_enterprise/concerns/models/team.rb +67 -0
- data/lib/mno_enterprise/core.rb +22 -3
- data/lib/mno_enterprise/impac_client.rb +19 -0
- data/lib/mno_enterprise/mail_adapters/mandrill_adapter.rb +4 -1
- data/lib/mno_enterprise/testing_support/factories/app_review.rb +51 -0
- data/lib/mno_enterprise/testing_support/factories/apps.rb +12 -10
- data/lib/mno_enterprise/testing_support/factories/identity.rb +9 -0
- data/lib/mno_enterprise/testing_support/factories/impac/alerts.rb +18 -0
- data/lib/mno_enterprise/testing_support/factories/impac/kpis.rb +5 -9
- data/lib/mno_enterprise/testing_support/factories/users.rb +4 -0
- data/lib/mno_enterprise/testing_support/jpi_v1_test_helper.rb +39 -0
- data/lib/mno_enterprise/testing_support/organizations_shared_helpers.rb +2 -1
- data/lib/mno_enterprise/testing_support/shared_examples/jpi_v1_admin.rb +38 -0
- data/lib/mno_enterprise/version.rb +1 -1
- data/spec/lib/devise/model/remote_authenticable_spec.rb +76 -0
- data/spec/lib/mno_enterprise/impac_client_spec.rb +31 -0
- data/spec/mno_enterprise_spec.rb +7 -8
- data/spec/models/mno_enterprise/app_spec.rb +1 -1
- data/spec/models/mno_enterprise/identity_spec.rb +39 -0
- data/spec/models/mno_enterprise/organization_spec.rb +19 -2
- data/spec/models/mno_enterprise/user_spec.rb +238 -0
- metadata +86 -22
@@ -3,24 +3,28 @@ module Her
|
|
3
3
|
module Model
|
4
4
|
module Associations
|
5
5
|
class AssociationProxy < (ActiveSupport.const_defined?('ProxyObject') ? ActiveSupport::ProxyObject : ActiveSupport::BasicObject)
|
6
|
-
|
6
|
+
|
7
7
|
install_proxy_methods :association,
|
8
8
|
:build, :create, :update, :destroy, :where, :find, :all, :assign_nested_attributes, :reload, :order, :order_by, :limit, :skip
|
9
|
-
|
10
|
-
|
9
|
+
|
10
|
+
# Returns true if the association has been loaded, otherwise false.
|
11
|
+
def loaded?
|
12
|
+
!!association.instance_variable_get('@cached_result')
|
13
|
+
end
|
14
|
+
|
11
15
|
def method_missing(name, *args, &block)
|
12
16
|
if :object_id == name # avoid redefining object_id
|
13
17
|
return association.fetch.object_id
|
14
18
|
end
|
15
|
-
|
19
|
+
|
16
20
|
# Check if a class scope has previously been defined
|
17
21
|
begin
|
18
22
|
if Relation.scopes.keys.grep(::Regexp.new(name.to_s)).any?
|
19
23
|
return self.association.send(name,*args,&block)
|
20
24
|
end
|
21
|
-
rescue ::NoMethodError => e
|
25
|
+
rescue ::NoMethodError => e
|
22
26
|
end
|
23
|
-
|
27
|
+
|
24
28
|
# create a proxy to the fetched object's method
|
25
29
|
# https://github.com/remiprev/her/pull/377
|
26
30
|
AssociationProxy.install_proxy_methods 'association.fetch', name
|
@@ -98,7 +98,7 @@ module MnoEnterprise::Concerns::Controllers::Auth::ConfirmationsController
|
|
98
98
|
sign_in resource, bypass: true
|
99
99
|
set_flash_message(:notice, :confirmed) if is_flashing_format?
|
100
100
|
yield(:success,resource) if block_given?
|
101
|
-
MnoEnterprise::EventLogger.info('user_confirm', resource.id, 'User confirmed',
|
101
|
+
MnoEnterprise::EventLogger.info('user_confirm', resource.id, 'User confirmed', resource)
|
102
102
|
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource, new_user: true) }
|
103
103
|
else
|
104
104
|
yield(:error,resource) if block_given?
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# This controller is used to handle the authentication (+creation) of external
|
2
|
+
# users via OpenID (e.g: QuickBooks OpenID)
|
3
|
+
#
|
4
|
+
# When users click on the "sign in with <provider>" button, they get redirected
|
5
|
+
# to the authorize endpoint (/users/auth/:provider - e.g: /users/auth/intuit).
|
6
|
+
# The action (handled by parent controller OmniauthCallbacksController) prepares
|
7
|
+
# the callback url then redirects the user to the OpenID provider (E.g: Intuit)
|
8
|
+
# for authentication.
|
9
|
+
#
|
10
|
+
# Once authentication has been performed at the OpenID provider level (e.g: Intuit)
|
11
|
+
# the user gets redirected to the callback endpoint (/users/auth/:provider/callback)
|
12
|
+
# The provider parameter in the url (E.g: intuit) gets automatically redirected to a
|
13
|
+
# controller action with the same name (handled by parent controller OmniauthCallbacksController)
|
14
|
+
# as you can see below with intuit.
|
15
|
+
#
|
16
|
+
# Then provider specific action then handles the (creation +) authentication of the user.
|
17
|
+
# Also, it automatically adds the right applications to the user dashboard (e.g: QuickBooks for
|
18
|
+
# Intuit)
|
19
|
+
#
|
20
|
+
# Intuit:
|
21
|
+
# --------
|
22
|
+
# For intuit, the authorize endpoint is be bypassed when the user clicks "try Maestrano" from
|
23
|
+
# the Intuit marketplace. The user automatically lands on the callback endpoints with a parameter
|
24
|
+
# in the url called 'qb_initiated'. This parameter is used to automatically trigger the retrieval
|
25
|
+
# of the oauth token in the background via javascript (by storing the temporary grant url in session)
|
26
|
+
#
|
27
|
+
# On Intuit, it is also possible to directly choose one of the apps proposed by Maestrano (E.g: 'SugarCRM
|
28
|
+
# by Maestrano'). In this case, an 'app' attribute containing the app nid (named id - e.g: 'sugarcrm') is
|
29
|
+
# added to the url parameters. The action then setup the app automatically (along with QuickBooks).
|
30
|
+
#
|
31
|
+
#
|
32
|
+
module MnoEnterprise::Concerns::Controllers::Auth::OmniauthCallbacksController
|
33
|
+
extend ActiveSupport::Concern
|
34
|
+
|
35
|
+
#==================================================================
|
36
|
+
# Included methods
|
37
|
+
#==================================================================
|
38
|
+
included do
|
39
|
+
skip_filter :verify_authenticity_token, only: [:intuit]
|
40
|
+
|
41
|
+
providers = Devise.omniauth_providers & %i(linkedin google facebook)
|
42
|
+
|
43
|
+
providers.each do |provider|
|
44
|
+
provides_callback_for provider
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#==================================================================
|
49
|
+
# Class methods
|
50
|
+
#==================================================================
|
51
|
+
module ClassMethods
|
52
|
+
def provides_callback_for(provider)
|
53
|
+
class_eval %Q{
|
54
|
+
def #{provider}
|
55
|
+
auth = env["omniauth.auth"]
|
56
|
+
opts = { orga_on_create: create_orga_on_user_creation(auth.info.email) }
|
57
|
+
|
58
|
+
@user = MnoEnterprise::User.find_for_oauth(auth, opts, current_user)
|
59
|
+
|
60
|
+
if @user.persisted?
|
61
|
+
sign_in_and_redirect @user, event: :authentication
|
62
|
+
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
|
63
|
+
else
|
64
|
+
session["devise.#{provider}_data"] = env["omniauth.auth"]
|
65
|
+
redirect_to new_user_registration_url
|
66
|
+
end
|
67
|
+
end
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#==================================================================
|
73
|
+
# Instance methods
|
74
|
+
#==================================================================
|
75
|
+
# GET|POST /users/auth/:action/callback
|
76
|
+
def intuit
|
77
|
+
auth = request.env['omniauth.auth']
|
78
|
+
opts = {
|
79
|
+
orga_on_create: create_orga_on_user_creation(auth.info.email),
|
80
|
+
authorized_link_to_email: session['omniauth.intuit.authorized_link_to_email']
|
81
|
+
}
|
82
|
+
|
83
|
+
# Try to find via intuit
|
84
|
+
begin
|
85
|
+
@user = MnoEnterprise::User.find_for_oauth(auth, opts, current_user)
|
86
|
+
rescue SecurityError
|
87
|
+
# Intuit email is NOT a confirmed email. Therefore we need to ask the user to
|
88
|
+
# login the old fashion to make sure it is the right user!
|
89
|
+
session["omniauth.intuit.request_account_link"] = true
|
90
|
+
redirect_to new_user_session_path, notice: "Please sign in using your regular Maestrano account to confirm that you want to link it to your Intuit account"
|
91
|
+
return
|
92
|
+
end
|
93
|
+
|
94
|
+
# Cleanup any temporary omniauth.intuit session
|
95
|
+
cleanup_intuit_session
|
96
|
+
|
97
|
+
if @user && @user.persisted?
|
98
|
+
# Automatically adds a QuickBooks app (and any other app passed via :app param)
|
99
|
+
# to the user orga
|
100
|
+
# Only for new users for which an orga was created (not an invited user
|
101
|
+
# typically)
|
102
|
+
app_instances = setup_apps(@user,['quickbooks',params[:app]], oauth_keyset: params[:app])
|
103
|
+
qb_instance = app_instances.first
|
104
|
+
|
105
|
+
# On Intuit, Mno is configured to add qb_initiated=true if the user
|
106
|
+
# comes directly from apps.com (This is a different workflow from using
|
107
|
+
# the QuickBooks connect button because we're supposed to trigger the
|
108
|
+
# oauth workflow directly via javascript using directConnectToIntuit)
|
109
|
+
# Here we store in session the fact that we need to trigger an oauth
|
110
|
+
# workflow via directConnectToIntuit
|
111
|
+
# ----
|
112
|
+
# See layouts/partners/intuit for more info. The session param set
|
113
|
+
# below get reset in the view.
|
114
|
+
#
|
115
|
+
if params[:qb_initiated] && qb_instance && !qb_instance.oauth_keys_valid?
|
116
|
+
session[:qb_direct_connect_grant_url] = authorize_webhook_oauth_url(qb_instance.uid)
|
117
|
+
end
|
118
|
+
|
119
|
+
# The above methods trigger many different hooks which
|
120
|
+
# may impact the user (typically user workspace). It is safer
|
121
|
+
# to reload the user before continuing so that the picture
|
122
|
+
# is up to date
|
123
|
+
@user.reload
|
124
|
+
|
125
|
+
# Check whether we should redirect the user to a specific
|
126
|
+
# url
|
127
|
+
redirect_url = session.delete(:openid_previous_url) || MnoEnterprise.router.dashboard_path || main_app.root_path
|
128
|
+
|
129
|
+
sign_in @user
|
130
|
+
redirect_to redirect_url, event: :authentication
|
131
|
+
|
132
|
+
set_flash_message(:notice, :success, kind: "Intuit") if is_navigational_format?
|
133
|
+
else
|
134
|
+
session["devise.intuit_data"] = request.env["omniauth.auth"]
|
135
|
+
redirect_to home_url, "ng-controller" => "MnoSignupProcessCtrl", "ng-click" => "startProcess()"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#================================================
|
140
|
+
# Private methods
|
141
|
+
#================================================
|
142
|
+
private
|
143
|
+
|
144
|
+
def cleanup_intuit_session
|
145
|
+
session.delete("omniauth.intuit.passthru_email")
|
146
|
+
session.delete("omniauth.intuit.request_account_link")
|
147
|
+
end
|
148
|
+
|
149
|
+
# Whether to create an orga on user creation
|
150
|
+
def create_orga_on_user_creation(user_email = nil)
|
151
|
+
return false if user_email.blank?
|
152
|
+
return false if MnoEnterprise::User.exists?(email: user_email)
|
153
|
+
|
154
|
+
# First check previous url to see if the user
|
155
|
+
# was trying to accept an orga
|
156
|
+
if !session[:previous_url].blank? && (r = session[:previous_url].match(/\/orga_invites\/(\d+)\?token=(\w+)/))
|
157
|
+
invite_params = { id: r.captures[0].to_i, token: r.captures[1] }
|
158
|
+
return false if OrgInvite.where(invite_params).any?
|
159
|
+
end
|
160
|
+
|
161
|
+
# Get remaining invites via email address
|
162
|
+
return MnoEnterprise::OrgInvite.where(user_email: user_email).empty?
|
163
|
+
end
|
164
|
+
|
165
|
+
# Create or find the apps provided in argument
|
166
|
+
# Accept an array of app nid (named id - e.g: 'quickbooks')
|
167
|
+
# opts:
|
168
|
+
# oauth_keyset: If a oauth_keyset is provided then it will be added to the
|
169
|
+
# oauth_keys of any app that is oauth ready (QuickBooks for example)
|
170
|
+
#
|
171
|
+
# Return an array of app instances (found or created)
|
172
|
+
def setup_apps(user = nil, app_nids = [], opts = {})
|
173
|
+
return [] unless user
|
174
|
+
return [] unless (user.organizations.reload.count == 1)
|
175
|
+
return [] unless (org = user.organizations.first)
|
176
|
+
return [] unless MnoEnterprise::Ability.new(user).can?(:edit,org)
|
177
|
+
|
178
|
+
results = []
|
179
|
+
|
180
|
+
apps = MnoEnterprise::App.where('nid.in' => app_nids.compact)
|
181
|
+
existing = org.app_instances.active.index_by(&:app_id)
|
182
|
+
|
183
|
+
# For each app nid (which is not nil), try to find an existing instance or create one
|
184
|
+
apps.each do |app|
|
185
|
+
if (app_instance = existing[app.id])
|
186
|
+
results << app_instance
|
187
|
+
else
|
188
|
+
# Provision instance and add to results
|
189
|
+
app_instance = org.app_instances.create(product: app.nid)
|
190
|
+
results << app_instance
|
191
|
+
MnoEnterprise::EventLogger.info('app_add', user.id, 'App added', app_instance)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Add oauth keyset if defined and app_instance is
|
195
|
+
# oauth ready and does not have a valid set of oauth keys
|
196
|
+
if app_instance && opts[:oauth_keyset].present? && !app_instance.oauth_keys_valid?
|
197
|
+
app_instance.oauth_keys = { keyset: opts[:oauth_keyset] }
|
198
|
+
app_instance.save
|
199
|
+
end
|
200
|
+
end
|
201
|
+
return results
|
202
|
+
end
|
203
|
+
end
|
@@ -109,6 +109,34 @@ module MnoEnterprise::Concerns::Models::Ability
|
|
109
109
|
!!user.role(org) && ['Super Admin', 'Admin'].include?(user.role(org))
|
110
110
|
end
|
111
111
|
end
|
112
|
+
|
113
|
+
can :manage_dashboard, MnoEnterprise::Impac::Dashboard do |dashboard|
|
114
|
+
if dashboard.owner_type == "Organization"
|
115
|
+
# The current user is a member of the organization that owns the dashboard that has the kpi attached to
|
116
|
+
owner = MnoEnterprise::Organization.find(dashboard.owner_id)
|
117
|
+
owner && !!user.role(owner)
|
118
|
+
elsif dashboard.owner_type == "User"
|
119
|
+
# The current user is the owner of the dashboard that has the kpi attached to
|
120
|
+
dashboard.owner_id == user.id
|
121
|
+
else
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
can :manage_widget, MnoEnterprise::Impac::Widget do |widget|
|
127
|
+
dashboard = widget.dashboard
|
128
|
+
authorize! :manage_dashboard, dashboard
|
129
|
+
end
|
130
|
+
|
131
|
+
can :manage_kpi, MnoEnterprise::Impac::Kpi do |kpi|
|
132
|
+
dashboard = kpi.dashboard
|
133
|
+
authorize! :manage_dashboard, dashboard
|
134
|
+
end
|
135
|
+
|
136
|
+
can :manage_alert, MnoEnterprise::Impac::Alert do |alert|
|
137
|
+
kpi = alert.kpi
|
138
|
+
authorize! :manage_kpi, kpi
|
139
|
+
end
|
112
140
|
end
|
113
141
|
|
114
142
|
# Abilities for admin user
|
@@ -40,13 +40,13 @@ module MnoEnterprise::Concerns::Models::AppInstance
|
|
40
40
|
included do
|
41
41
|
attributes :id, :uid, :name, :status, :app_id, :created_at, :updated_at, :started_at, :stack, :owner_id,
|
42
42
|
:owner_type, :terminated_at, :stopped_at, :billing_type, :autostop_at, :autostop_interval,
|
43
|
-
:next_status, :soa_enabled, :oauth_keys_valid, :
|
43
|
+
:next_status, :soa_enabled, :oauth_company, :oauth_keys, :oauth_keys_valid, :free_trial_end_at, :per_user_licence, :active_licences_count
|
44
44
|
|
45
45
|
#==============================================================
|
46
46
|
# Constants
|
47
47
|
#==============================================================
|
48
|
-
ACTIVE_STATUSES = [:running
|
49
|
-
TERMINATION_STATUSES = [:terminating
|
48
|
+
ACTIVE_STATUSES = [:running, :stopped, :staged, :provisioning, :starting, :stopping, :updating]
|
49
|
+
TERMINATION_STATUSES = [:terminating, :terminated]
|
50
50
|
|
51
51
|
#==============================================================
|
52
52
|
# Associations
|
@@ -55,7 +55,7 @@ module MnoEnterprise::Concerns::Models::AppInstance
|
|
55
55
|
belongs_to :app, class_name: 'MnoEnterprise::App'
|
56
56
|
|
57
57
|
# Define connector_stack?, cloud_stack? etc. methods
|
58
|
-
[:cube
|
58
|
+
[:cube, :cloud, :connector].each do |stackname|
|
59
59
|
define_method("#{stackname}_stack?") do
|
60
60
|
self.stack == stackname.to_s
|
61
61
|
end
|
@@ -89,8 +89,26 @@ module MnoEnterprise::Concerns::Models::AppInstance
|
|
89
89
|
ACTIVE_STATUSES.include?(self.status.to_sym)
|
90
90
|
end
|
91
91
|
|
92
|
+
def per_user_licence?
|
93
|
+
self.per_user_licence
|
94
|
+
end
|
95
|
+
|
96
|
+
def under_free_trial?
|
97
|
+
self.under_free_trial
|
98
|
+
end
|
99
|
+
|
92
100
|
def running?
|
93
101
|
self.status == 'running'
|
94
102
|
end
|
95
103
|
|
104
|
+
def to_audit_event
|
105
|
+
{
|
106
|
+
id: id,
|
107
|
+
uid: uid,
|
108
|
+
name: name,
|
109
|
+
app_nid: app ? app.nid : nil
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
|
96
114
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module MnoEnterprise::Concerns::Models::IntercomUser
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
#==================================================================
|
7
|
+
# Included methods
|
8
|
+
#==================================================================
|
9
|
+
# 'included do' causes the included code to be evaluated in the
|
10
|
+
# context where it is included rather than being executed in the module's context
|
11
|
+
included do
|
12
|
+
end
|
13
|
+
|
14
|
+
#==================================================================
|
15
|
+
# Class methods
|
16
|
+
#==================================================================
|
17
|
+
module ClassMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
#==================================================================
|
21
|
+
# Instance methods
|
22
|
+
#==================================================================
|
23
|
+
# Return intercom user hash
|
24
|
+
# This is used in secure mode
|
25
|
+
def intercom_user_hash
|
26
|
+
OpenSSL::HMAC.hexdigest('sha256', MnoEnterprise.intercom_api_secret, (self.id || self.email).to_s) if MnoEnterprise.intercom_api_secret
|
27
|
+
end
|
28
|
+
end
|
@@ -51,6 +51,7 @@ module MnoEnterprise::Concerns::Models::Organization
|
|
51
51
|
has_one :credit_card, class_name: 'MnoEnterprise::CreditCard'
|
52
52
|
has_many :teams, class_name: 'MnoEnterprise::Team'
|
53
53
|
has_many :dashboards, class_name: 'MnoEnterprise::Impac::Dashboard'
|
54
|
+
has_many :widgets, class_name: 'MnoEnterprise::Impac::Widget'
|
54
55
|
has_one :raw_last_invoice, class_name: 'MnoEnterprise::Invoice', path: '/last_invoice'
|
55
56
|
has_one :app_instances_sync, class_name: 'MnoEnterprise::AppInstancesSync'
|
56
57
|
end
|
@@ -73,7 +74,7 @@ module MnoEnterprise::Concerns::Models::Organization
|
|
73
74
|
# @params [Boolean] show_staged Also displayed staged invites (ie: not sent)
|
74
75
|
def members(show_staged=false)
|
75
76
|
invites = show_staged ? self.org_invites.active_or_staged : self.org_invites.active
|
76
|
-
[self.users, invites].flatten
|
77
|
+
[self.users, invites.to_a].flatten
|
77
78
|
end
|
78
79
|
|
79
80
|
# Add a user to the organization with the provided role
|
@@ -102,4 +103,16 @@ module MnoEnterprise::Concerns::Models::Organization
|
|
102
103
|
def update_user(user, role = 'Member')
|
103
104
|
self.users.update(id: user.id, role: role)
|
104
105
|
end
|
106
|
+
|
107
|
+
def to_audit_event
|
108
|
+
{
|
109
|
+
id: id,
|
110
|
+
uid: uid,
|
111
|
+
name: name
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def payment_restriction
|
116
|
+
meta_data && meta_data['payment_restriction']
|
117
|
+
end
|
105
118
|
end
|
@@ -0,0 +1,67 @@
|
|
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::Concerns::Models::Team
|
15
|
+
extend ActiveSupport::Concern
|
16
|
+
|
17
|
+
#==================================================================
|
18
|
+
# Included methods
|
19
|
+
#==================================================================
|
20
|
+
# 'included do' causes the included code to be evaluated in the
|
21
|
+
# context where it is included rather than being executed in the module's context
|
22
|
+
included do
|
23
|
+
attributes :id, :name, :organization_id
|
24
|
+
|
25
|
+
#=====================================
|
26
|
+
# Associations
|
27
|
+
#=====================================
|
28
|
+
belongs_to :organization, class_name: 'MnoEnterprise::Organization'
|
29
|
+
has_many :users, class_name: 'MnoEnterprise::User'
|
30
|
+
has_many :app_instances, class_name: 'MnoEnterprise::AppInstance'
|
31
|
+
end
|
32
|
+
|
33
|
+
#==================================================================
|
34
|
+
# Class methods
|
35
|
+
#==================================================================
|
36
|
+
module ClassMethods
|
37
|
+
# def some_class_method
|
38
|
+
# 'some text'
|
39
|
+
# end
|
40
|
+
end
|
41
|
+
|
42
|
+
#==================================================================
|
43
|
+
# Instance methods
|
44
|
+
#==================================================================
|
45
|
+
# Add a user to the team
|
46
|
+
# TODO: specs
|
47
|
+
def add_user(user)
|
48
|
+
self.users.create(id: user.id)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Remove a user from the team
|
52
|
+
# TODO: specs
|
53
|
+
def remove_user(user)
|
54
|
+
self.users.destroy(id: user.id)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Set the app_instance permissions of this team
|
58
|
+
# Accept a collection of hashes or an array of ids
|
59
|
+
# TODO: specs
|
60
|
+
def set_access_to(collection_or_array)
|
61
|
+
# Empty arrays do not seem to be passed in the request. Force value in this case
|
62
|
+
list = collection_or_array.empty? ? [""] : collection_or_array
|
63
|
+
self.put(data: { set_access_to: list })
|
64
|
+
self.reload
|
65
|
+
self
|
66
|
+
end
|
67
|
+
end
|
data/lib/mno_enterprise/core.rb
CHANGED
@@ -21,6 +21,7 @@ require "her_extension/model/associations/belongs_to_association"
|
|
21
21
|
require "her_extension/middleware/mnoe_api_v1_parse_json"
|
22
22
|
require "her_extension/middleware/mnoe_raise_error"
|
23
23
|
require "faraday_middleware"
|
24
|
+
require "httparty"
|
24
25
|
require "mno_enterprise/engine"
|
25
26
|
|
26
27
|
require 'mno_enterprise/database_extendable'
|
@@ -29,8 +30,7 @@ require 'mno_enterprise/database_extendable'
|
|
29
30
|
require 'config'
|
30
31
|
require 'figaro'
|
31
32
|
|
32
|
-
require
|
33
|
-
|
33
|
+
require 'mandrill_client'
|
34
34
|
require 'accountingjs_serializer'
|
35
35
|
|
36
36
|
module MnoEnterprise
|
@@ -52,7 +52,7 @@ module MnoEnterprise
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def terms_url
|
55
|
-
@terms_url || '
|
55
|
+
@terms_url || '/mnoe/terms'
|
56
56
|
end
|
57
57
|
|
58
58
|
def admin_path
|
@@ -213,6 +213,23 @@ module MnoEnterprise
|
|
213
213
|
mattr_accessor :google_tag_container
|
214
214
|
@@google_tag_container = nil
|
215
215
|
|
216
|
+
mattr_accessor :intercom_app_id
|
217
|
+
@@intercom_app_id = nil
|
218
|
+
|
219
|
+
mattr_accessor :intercom_api_secret
|
220
|
+
@@intercom_api_secret = nil
|
221
|
+
|
222
|
+
mattr_accessor :intercom_api_key
|
223
|
+
@@intercom_api_key = nil
|
224
|
+
|
225
|
+
mattr_accessor :intercom_token
|
226
|
+
@@intercom_token = nil
|
227
|
+
|
228
|
+
# Define if Intercom is enabled. Only if the gem intercom is present
|
229
|
+
def self.intercom_enabled?
|
230
|
+
defined?(::Intercom) && ((intercom_app_id && intercom_api_key) || intercom_token)
|
231
|
+
end
|
232
|
+
|
216
233
|
#====================================
|
217
234
|
# Layout & Styling
|
218
235
|
#====================================
|
@@ -248,6 +265,8 @@ module MnoEnterprise
|
|
248
265
|
@@style
|
249
266
|
end
|
250
267
|
|
268
|
+
|
269
|
+
|
251
270
|
# Default way to setup MnoEnterprise. Run rails generate mno-enterprise:install to create
|
252
271
|
# a fresh initializer with all configuration values.
|
253
272
|
def self.configure
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MnoEnterprise
|
2
|
+
class ImpacClient
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
def self.host
|
6
|
+
"#{Settings.impac.protocol}://#{Settings.impac.host}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.endpoint_url(endpoint, params)
|
10
|
+
"#{File.join(host,endpoint)}?#{params.to_query}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.send_get(endpoint, params, opts={})
|
14
|
+
url = endpoint_url(endpoint, params)
|
15
|
+
get(url, opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -16,7 +16,10 @@ module MnoEnterprise
|
|
16
16
|
# Prepare message from args
|
17
17
|
message = { from_name: from[:name], from_email: from[:email]}
|
18
18
|
message[:to] = [to].flatten.map { |t| {name: t[:name], email: t[:email], type: (t[:type] || :to) } }
|
19
|
-
|
19
|
+
|
20
|
+
# Sanitize merge vars
|
21
|
+
full_sanitizer = Rails::Html::FullSanitizer.new
|
22
|
+
message[:global_merge_vars] = vars.map { |k,v| {name: k.to_s, content: full_sanitizer.sanitize(v)} }
|
20
23
|
|
21
24
|
# Merge additional mandrill options
|
22
25
|
message.merge!(opts)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Read about factories at https://github.com/thoughtbot/factory_girl
|
2
|
+
|
3
|
+
FactoryGirl.define do
|
4
|
+
factory :mno_enterprise_app_review, :class => 'AppReview' do
|
5
|
+
|
6
|
+
factory :app_review, class: MnoEnterprise::AppReview do
|
7
|
+
sequence(:id)
|
8
|
+
description 'Some Description'
|
9
|
+
status 'approved'
|
10
|
+
rating 3
|
11
|
+
app_id 'app-id'
|
12
|
+
app_name 'the app'
|
13
|
+
user_id 'usr-11'
|
14
|
+
user_name 'Jean Bon'
|
15
|
+
organization_id 'org-11'
|
16
|
+
organization_name 'Organization 11'
|
17
|
+
created_at 3.days.ago
|
18
|
+
updated_at 1.hour.ago
|
19
|
+
edited true
|
20
|
+
edited_by_name 'Jane Dale'
|
21
|
+
edited_by_admin_role 'admin'
|
22
|
+
edited_by_id 1
|
23
|
+
|
24
|
+
user_admin_role 'admin'
|
25
|
+
# Properly build the resource with Her
|
26
|
+
initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
|
27
|
+
|
28
|
+
factory :app_feedback, class: MnoEnterprise::AppFeedback do
|
29
|
+
type 'Feedback'
|
30
|
+
sequence(:description) { |n| "Feedback ##{n}" }
|
31
|
+
end
|
32
|
+
|
33
|
+
factory :app_question, class: MnoEnterprise::AppQuestion do
|
34
|
+
type 'Question'
|
35
|
+
sequence(:description) { |n| "Question ##{n}" }
|
36
|
+
end
|
37
|
+
|
38
|
+
factory :app_comment, class: MnoEnterprise::AppComment do
|
39
|
+
type 'Comment'
|
40
|
+
sequence(:description) { |n| "Comment ##{n}" }
|
41
|
+
feedback_id 'feedback-id'
|
42
|
+
end
|
43
|
+
|
44
|
+
factory :app_answer, class: MnoEnterprise::AppAnswer do
|
45
|
+
type 'Answer'
|
46
|
+
sequence(:description) { |n| "Answer ##{n}" }
|
47
|
+
question_id 'question-id'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -5,7 +5,7 @@ FactoryGirl.define do
|
|
5
5
|
sequence(:id) { |n| n }
|
6
6
|
sequence(:name) { |n| "TestApp#{n}" }
|
7
7
|
nid { name.parameterize }
|
8
|
-
|
8
|
+
|
9
9
|
description "This is a description"
|
10
10
|
created_at 1.day.ago
|
11
11
|
updated_at 2.hours.ago
|
@@ -14,30 +14,32 @@ FactoryGirl.define do
|
|
14
14
|
slug { "#{id}-myapp" }
|
15
15
|
categories ["CRM"]
|
16
16
|
tags ['Foo', 'Bar']
|
17
|
-
key_benefits ['Super','Hyper','Good']
|
18
|
-
key_features ['Super','Hyper','Good']
|
19
|
-
testimonials [{text:'Bla', company:'Doe Pty Ltd', author: 'John'}]
|
17
|
+
key_benefits ['Super', 'Hyper', 'Good']
|
18
|
+
key_features ['Super', 'Hyper', 'Good']
|
19
|
+
testimonials [{text: 'Bla', company: 'Doe Pty Ltd', author: 'John'}]
|
20
20
|
worldwide_usage 120000
|
21
21
|
tiny_description "A great app"
|
22
22
|
stack 'cube'
|
23
23
|
terms_url "http://opensource.org/licenses/MIT"
|
24
24
|
appinfo { {} }
|
25
|
+
average_rating { rand(1..5) }
|
25
26
|
sequence(:rank) { |n| n }
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
running_instances_count { rand(0..10) }
|
28
|
+
pricing_plans { {
|
29
|
+
'default' => [{name: 'Monthly Plan', price: '20.0', currency: 'AUD', factor: '/month'}]
|
30
|
+
} }
|
29
31
|
|
30
32
|
trait :cloud do
|
31
33
|
stack 'cloud'
|
32
34
|
end
|
33
|
-
|
35
|
+
|
34
36
|
trait :connector do
|
35
37
|
stack 'connector'
|
36
38
|
end
|
37
|
-
|
39
|
+
|
38
40
|
factory :cloud_app, traits: [:cloud]
|
39
41
|
factory :connector_app, traits: [:connector]
|
40
|
-
|
42
|
+
|
41
43
|
# Properly build the resource with Her
|
42
44
|
initialize_with { new(attributes).tap { |e| e.clear_attribute_changes! } }
|
43
45
|
end
|