api_models 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/lib/api_models/version.rb +5 -0
- data/lib/api_models.rb +68 -0
- data/lib/models/account.rb +39 -0
- data/lib/models/agent.rb +439 -0
- data/lib/models/app.rb +18 -0
- data/lib/models/app47_cache.rb +69 -0
- data/lib/models/app_active_build.rb +32 -0
- data/lib/models/app_policy.rb +24 -0
- data/lib/models/app_shield_policy.rb +35 -0
- data/lib/models/authorized_user_policy.rb +44 -0
- data/lib/models/b2b_app.rb +8 -0
- data/lib/models/cat/customer.rb +9 -0
- data/lib/models/cat/customer_device.rb +10 -0
- data/lib/models/cl/customer.rb +9 -0
- data/lib/models/cl/customer_device.rb +10 -0
- data/lib/models/commerce_app_store.rb +8 -0
- data/lib/models/concerns/app47_app_analyzable.rb +46 -0
- data/lib/models/concerns/app47_app_buildable.rb +46 -0
- data/lib/models/concerns/app47_app_configurable.rb +34 -0
- data/lib/models/concerns/app47_app_policies.rb +59 -0
- data/lib/models/concerns/app47_cdn_url.rb +50 -0
- data/lib/models/concerns/app47_email_sendable.rb +29 -0
- data/lib/models/concerns/app47_logger.rb +109 -0
- data/lib/models/concerns/checkin_able.rb +28 -0
- data/lib/models/concerns/searchable.rb +54 -0
- data/lib/models/configuration_constraint.rb +22 -0
- data/lib/models/configuration_group.rb +50 -0
- data/lib/models/configuration_rule.rb +58 -0
- data/lib/models/device.rb +124 -0
- data/lib/models/external_app.rb +15 -0
- data/lib/models/gehc/customer.rb +17 -0
- data/lib/models/gehc/customer_device.rb +23 -0
- data/lib/models/group.rb +40 -0
- data/lib/models/internal_app.rb +11 -0
- data/lib/models/metric_data_job.rb +16 -0
- data/lib/models/option.rb +37 -0
- data/lib/models/pin_code_policy.rb +50 -0
- data/lib/models/platform.rb +26 -0
- data/lib/models/platform_model.rb +14 -0
- data/lib/models/product_app.rb +22 -0
- data/lib/models/product_support_app.rb +19 -0
- data/lib/models/queue_manager.rb +67 -0
- data/lib/models/redis_configuration.rb +137 -0
- data/lib/models/sso_ad_group.rb +7 -0
- data/lib/models/sso_ad_server.rb +13 -0
- data/lib/models/sso_ad_user.rb +13 -0
- data/lib/models/sso_azure_server.rb +18 -0
- data/lib/models/sso_directory_group.rb +13 -0
- data/lib/models/sso_directory_server.rb +21 -0
- data/lib/models/sso_google_server.rb +13 -0
- data/lib/models/sso_ldap_group.rb +8 -0
- data/lib/models/sso_ldap_rest_server.rb +34 -0
- data/lib/models/sso_ldap_server.rb +19 -0
- data/lib/models/sso_ldap_user.rb +7 -0
- data/lib/models/sso_oauth_server.rb +16 -0
- data/lib/models/sso_saml_v2_server.rb +25 -0
- data/lib/models/sso_server.rb +38 -0
- data/lib/models/sso_user.rb +13 -0
- data/lib/models/system_configuration.rb +92 -0
- data/lib/models/time_bomb_policy.rb +138 -0
- data/lib/models/user.rb +53 -0
- data/lib/models/user_app_permission.rb +16 -0
- data/lib/models/user_device.rb +83 -0
- data/lib/models/version_management_policy.rb +113 -0
- data/lib/models/web_app.rb +12 -0
- metadata +408 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Easily fetch the redis configuration from redis.yml in the config directory.
|
5
|
+
# There are several formats to support
|
6
|
+
#
|
7
|
+
# URL with single server
|
8
|
+
# development:
|
9
|
+
# url: 'redis://localhost:6379/0'
|
10
|
+
#
|
11
|
+
# Host/port combination for single server
|
12
|
+
# development:
|
13
|
+
# host: localhost
|
14
|
+
# port: 6379
|
15
|
+
# db: 0
|
16
|
+
#
|
17
|
+
# Sentinel
|
18
|
+
# development:
|
19
|
+
# master: production
|
20
|
+
# sentinels:
|
21
|
+
# - host1:6379
|
22
|
+
# - host2:6379
|
23
|
+
# - host3:6379
|
24
|
+
# role: master
|
25
|
+
#
|
26
|
+
# Available for all options
|
27
|
+
# connect_timeout: 0.2
|
28
|
+
# read_timeout: 0.2
|
29
|
+
# write_timeout: 0.2
|
30
|
+
# timeout: 1
|
31
|
+
#
|
32
|
+
class RedisConfiguration
|
33
|
+
#
|
34
|
+
# Methods are static
|
35
|
+
#
|
36
|
+
class << self
|
37
|
+
#
|
38
|
+
# Load the configuration using the given DB
|
39
|
+
#
|
40
|
+
# Trying first the URL, then host, then sentinel, then default method
|
41
|
+
#
|
42
|
+
def load(database = nil)
|
43
|
+
load_url(database) || load_host(database) || load_sentinel(database) || load_default(database)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
#
|
49
|
+
# Load the base set of parameters from the config file
|
50
|
+
#
|
51
|
+
def load_base(raw_config)
|
52
|
+
base = {}
|
53
|
+
%i[connect_timeout namespace read_timeout write_timeout timeout].each do |key|
|
54
|
+
base[key] = raw_config[key.to_s] unless raw_config[key.to_s].nil?
|
55
|
+
end
|
56
|
+
base
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Load a default configuration with the given database
|
61
|
+
#
|
62
|
+
def load_default(database = 0)
|
63
|
+
{ host: '127.0.0.1', port: 6_379, db: (database || 0) }
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Load the url
|
68
|
+
#
|
69
|
+
def load_url(database = 0)
|
70
|
+
raw_config = load_file
|
71
|
+
return if raw_config.nil? || raw_config['url'].nil?
|
72
|
+
|
73
|
+
config = load_base(raw_config)
|
74
|
+
config[:url] = raw_config['url']
|
75
|
+
config[:db] = (database || 0)
|
76
|
+
config
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Load the given host
|
81
|
+
#
|
82
|
+
def load_host(database = nil)
|
83
|
+
raw_config = load_file
|
84
|
+
return if raw_config.nil? || raw_config['host'].nil?
|
85
|
+
|
86
|
+
config = load_base(raw_config)
|
87
|
+
config[:host] = raw_config['host']
|
88
|
+
config[:port] = raw_config['port'] || 6_379
|
89
|
+
config[:db] = database || raw_config['db'] || 0
|
90
|
+
config
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Load the sentinel configuration
|
95
|
+
#
|
96
|
+
def load_sentinel(database = 0)
|
97
|
+
raw_config = load_file
|
98
|
+
return if raw_config.nil? || raw_config['master'].nil?
|
99
|
+
|
100
|
+
config = load_base(raw_config)
|
101
|
+
config[:url] = "redis://#{raw_config['master']}"
|
102
|
+
config[:sentinels] = raw_config['sentinels'].collect do |sentinel|
|
103
|
+
parts = sentinel.split(':')
|
104
|
+
host = parts.first
|
105
|
+
port = parts.count.eql?(2) ? parts.last : 26_379
|
106
|
+
{ host: host, port: port }
|
107
|
+
end
|
108
|
+
config[:db] = database || 0
|
109
|
+
config[:role] = raw_config['role'] || 'master'
|
110
|
+
config
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Return the config file
|
115
|
+
#
|
116
|
+
def config_file_path
|
117
|
+
File.expand_path([ENV['APP_CONFIG_DIR'], 'config', 'redis.yml'].compact.join('/'))
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Load the config/redis.yml and return the environment name
|
122
|
+
#
|
123
|
+
def load_file
|
124
|
+
config = nil
|
125
|
+
file_path = config_file_path
|
126
|
+
if File.exist? file_path
|
127
|
+
yaml_data = YAML.load_file(file_path)
|
128
|
+
config = yaml_data[ENV['RACK_ENV']] if yaml_data
|
129
|
+
end
|
130
|
+
|
131
|
+
config
|
132
|
+
rescue StandardError => error
|
133
|
+
puts "Error loading #{config_file_path} file", error
|
134
|
+
nil # return nothing if there is an error
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Azure SSO Server
|
5
|
+
#
|
6
|
+
class SsoAzureServer < SsoOauthServer
|
7
|
+
#
|
8
|
+
# Fields
|
9
|
+
#
|
10
|
+
field :tenant_id, type: String
|
11
|
+
|
12
|
+
#
|
13
|
+
# Google Server SSO name
|
14
|
+
#
|
15
|
+
def display_name
|
16
|
+
'Azure OAuth 2.0'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Base class for SSO directory (AD/LDAP) group
|
5
|
+
#
|
6
|
+
class SsoDirectoryGroup < Group
|
7
|
+
#
|
8
|
+
# Fields
|
9
|
+
#
|
10
|
+
field :dn, type: String
|
11
|
+
field :synchronize, type: Boolean, default: false
|
12
|
+
field :auto_approve_devices, type: Boolean, default: true
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Base class for SSO Directory servers, i.e., AD and LDAP
|
5
|
+
#
|
6
|
+
class SsoDirectoryServer < SsoServer
|
7
|
+
# fields
|
8
|
+
field :ssl, type: Boolean, default: false
|
9
|
+
field :currently_syncing, type: Boolean, default: false
|
10
|
+
field :server_name, type: String
|
11
|
+
field :port, type: String, default: '389'
|
12
|
+
field :username, type: String
|
13
|
+
field :password, type: String
|
14
|
+
field :frequency, type: String, default: '24'
|
15
|
+
field :last_sync_time, type: DateTime
|
16
|
+
field :last_sync_message, type: String
|
17
|
+
field :treebase, type: String
|
18
|
+
field :user_identifier, type: String
|
19
|
+
# relationships
|
20
|
+
has_many :groups, inverse_of: :sso_server, dependent: :nullify
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Interact with the Ldap Restful API for authentication
|
5
|
+
#
|
6
|
+
class SsoLdapRestServer < SsoServer
|
7
|
+
#
|
8
|
+
# Fields
|
9
|
+
#
|
10
|
+
field :auth_url, type: String
|
11
|
+
field :auth_bearer_token, type: String
|
12
|
+
field :auth_params, type: String
|
13
|
+
field :search_url, type: String
|
14
|
+
field :search_bearer_token, type: String
|
15
|
+
field :search_key_name, type: String, default: 'staff_id'
|
16
|
+
field :image_url, type: String
|
17
|
+
field :image_bearer_token, type: String
|
18
|
+
field :image_api_key, type: String
|
19
|
+
|
20
|
+
#
|
21
|
+
# Display name for this server, should be overriden by concrete implementations.
|
22
|
+
#
|
23
|
+
def display_name
|
24
|
+
'LDAP Rest API Server'
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Using the appropriate SSO server configuration obtain the
|
29
|
+
# the user profile address
|
30
|
+
#
|
31
|
+
def user_profile(user_id)
|
32
|
+
account.users.find(user_id)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Hold the configuration for LDAP synchronization, Using the ad_configuration logic to sync with server
|
5
|
+
#
|
6
|
+
class SsoLdapServer < SsoDirectoryServer
|
7
|
+
#
|
8
|
+
# Fields
|
9
|
+
#
|
10
|
+
field :user_filter, type: String, default: 'ou=users'
|
11
|
+
field :group_filter, type: String, default: 'ou=groups'
|
12
|
+
field :group_identifier, type: String, default: 'gidNumber'
|
13
|
+
#
|
14
|
+
# Return the display name for this server type
|
15
|
+
#
|
16
|
+
def display_name
|
17
|
+
'LDAP Server'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# OAuth 2.0 Server
|
5
|
+
#
|
6
|
+
class SsoOauthServer < SsoServer
|
7
|
+
# Fields
|
8
|
+
field :server_url, type: String
|
9
|
+
field :auth_url, type: String
|
10
|
+
field :token_url, type: String
|
11
|
+
field :profile_url, type: String
|
12
|
+
field :redirect_path, type: String, default: 'eas/auth/oauth'
|
13
|
+
field :client_id, type: String
|
14
|
+
field :client_secret, type: String
|
15
|
+
field :scopes, type: String
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# SSO SAML V2 Server
|
5
|
+
#
|
6
|
+
class SsoSamlV2Server < SsoServer
|
7
|
+
#
|
8
|
+
# Fields
|
9
|
+
#
|
10
|
+
field :idp_url, type: String
|
11
|
+
field :idp_xml, type: String
|
12
|
+
field :xml_last_fetched_at, type: Time, default: Time.now.utc
|
13
|
+
field :xml_cache_days, type: Integer, default: 365
|
14
|
+
field :name_identifier_format, type: String, default: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
|
15
|
+
field :name_attribute_key, type: String
|
16
|
+
field :email_attribute_key, type: String
|
17
|
+
field :groups_attribute_key, type: String
|
18
|
+
|
19
|
+
#
|
20
|
+
# Display name for this server, should be overriden by concrete implementations.
|
21
|
+
#
|
22
|
+
def display_name
|
23
|
+
'SAML V2 Server'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Base server container for an account which can have 0..N sso servers configured.
|
5
|
+
#
|
6
|
+
class SsoServer
|
7
|
+
include Mongoid::Document
|
8
|
+
include Mongoid::Timestamps
|
9
|
+
include App47Logger
|
10
|
+
#
|
11
|
+
# fields
|
12
|
+
#
|
13
|
+
field :active, type: Boolean, default: false
|
14
|
+
field :external_authentication, type: Boolean, default: false
|
15
|
+
field :name, type: String
|
16
|
+
field :default_user_invitation_message, type: String
|
17
|
+
#
|
18
|
+
# relationships
|
19
|
+
#
|
20
|
+
belongs_to :account, inverse_of: :sso_servers
|
21
|
+
has_many :users, inverse_of: :sso_server, dependent: :nullify, class_name: 'SsoUser'
|
22
|
+
has_many :groups, inverse_of: :sso_server, class_name: 'Group'
|
23
|
+
|
24
|
+
#
|
25
|
+
# Display name for this server, should be overriden by concrete implementations.
|
26
|
+
#
|
27
|
+
def display_name
|
28
|
+
'SSO Server'
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Using the appropriate SSO server configuration obtain the
|
33
|
+
# the user profile address, this should be implemented by the child class.
|
34
|
+
#
|
35
|
+
def user_profile(_code)
|
36
|
+
raise 'Failed to retrieve user profile from provider'
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Base class for a SSO User
|
5
|
+
#
|
6
|
+
class SsoUser < User
|
7
|
+
field :session_hours_length, type: Integer, default: 24
|
8
|
+
field :last_code, type: String
|
9
|
+
#
|
10
|
+
# Relationships
|
11
|
+
#
|
12
|
+
belongs_to :sso_server, class_name: 'SsoServer', inverse_of: :users
|
13
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# The System configuration. Various configuration items that can be updated/defined at run time instead
|
5
|
+
# of AppConfiguration that is defining things statically in code.
|
6
|
+
#
|
7
|
+
# In hindsight (being 20/20) this should have been named stack_configuration as now the stack UI is
|
8
|
+
# what allows the administrator to update/configure this information.
|
9
|
+
#
|
10
|
+
# Use of this class allows you to simply ask for the configuration parameter directly without
|
11
|
+
# first having to get an instance of it.
|
12
|
+
#
|
13
|
+
# SystemConfiguration.queue_impl #=> 'RedisQueue'
|
14
|
+
#
|
15
|
+
# This method only is allowed for accessors, you should NEVER set values on the SystemConfiguration
|
16
|
+
# unless you are updating via the Admin or Stack UI, or during testing to setup a specific configuration
|
17
|
+
# for that.
|
18
|
+
#
|
19
|
+
class SystemConfiguration
|
20
|
+
include Mongoid::Document
|
21
|
+
include Mongoid::Timestamps
|
22
|
+
|
23
|
+
field :environment, type: String, default: 'test'
|
24
|
+
# Queue Implementation
|
25
|
+
field :queue_impl, type: String, default: 'DelayedJob'
|
26
|
+
field :queue_base, type: String, default: 'test'
|
27
|
+
# AWS
|
28
|
+
field :aws_access_key_id, type: String
|
29
|
+
field :aws_secret_access_key, type: String
|
30
|
+
field :s3_bucket_name, type: String
|
31
|
+
field :cdn_url, type: String
|
32
|
+
# Redis
|
33
|
+
field :redis_queue_url, type: String, default: 'redis://localhost:6379/10'
|
34
|
+
# URLs
|
35
|
+
field :webui_url, type: String, default: 'https://cirrus.app47.com'
|
36
|
+
field :api_url, type: String, default: 'https://api.app47.mobi'
|
37
|
+
field :slack_api_url, type: String
|
38
|
+
field :company_name, type: String, default: 'App47'
|
39
|
+
field :api_version, type: String, default: '3.0'
|
40
|
+
#
|
41
|
+
# Validations
|
42
|
+
#
|
43
|
+
validates :environment, presence: true, uniqueness: true
|
44
|
+
#
|
45
|
+
# Call backs
|
46
|
+
#
|
47
|
+
after_save :clear_cache
|
48
|
+
|
49
|
+
class << self
|
50
|
+
#
|
51
|
+
# Fetch the system configuration based on environment name. First try the rails cache
|
52
|
+
# if not there, then go to the database.
|
53
|
+
#
|
54
|
+
# Also, if an argument error is thrown, then just fetch from the database.
|
55
|
+
#
|
56
|
+
def configuration
|
57
|
+
cache_key = "SystemConfiguration::#{ENV['RACK_ENV']}"
|
58
|
+
|
59
|
+
begin
|
60
|
+
config = App47Cache.get(cache_key) || SystemConfiguration.where(environment: ENV['RACK_ENV']).first
|
61
|
+
rescue ArgumentError
|
62
|
+
# This seems to happen in testing relative to Country, when it does, remove
|
63
|
+
# ourselves from the cache and return the DB entry.
|
64
|
+
App47Cache.delete cache_key
|
65
|
+
config = SystemConfiguration.where(environment: ENV['RACK_ENV']).first
|
66
|
+
end
|
67
|
+
if config.nil?
|
68
|
+
SystemConfiguration.create
|
69
|
+
else
|
70
|
+
App47Cache.set(cache_key, config, 300)
|
71
|
+
config
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_missing(method, *_args)
|
76
|
+
configuration.send method
|
77
|
+
end
|
78
|
+
|
79
|
+
def respond_to_missing?(method_name, _include_private = false)
|
80
|
+
SystemConfiguration.fields.include?(method_name.to_sym)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
#
|
87
|
+
# Clear the cache when the object is saved
|
88
|
+
#
|
89
|
+
def clear_cache
|
90
|
+
App47Cache.delete "SystemConfiguration::#{ENV['RACK_ENV']}"
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Time Bomb policy for this app
|
5
|
+
#
|
6
|
+
class TimeBombPolicy < AppPolicy
|
7
|
+
#
|
8
|
+
# Constants
|
9
|
+
#
|
10
|
+
OPTION_KILL = 'kill' unless defined? OPTION_KILL
|
11
|
+
OPTION_WIPE = 'wipe' unless defined? OPTION_WIPE
|
12
|
+
BOMB_OPTIONS = [OPTION_KILL, OPTION_WIPE].freeze unless defined? BOMB_OPTIONS
|
13
|
+
OPTION_AM = 'am' unless defined? OPTION_AM
|
14
|
+
OPTION_PM = 'pm' unless defined? OPTION_PM
|
15
|
+
TIME_OPTIONS = [OPTION_AM, OPTION_PM].freeze unless defined? TIME_OPTIONS
|
16
|
+
HOURS_TO_MS = 60 * 60 * 1000
|
17
|
+
UNIT_HOURS = 'hours' unless defined? UNIT_HOURS
|
18
|
+
UNIT_DAYS = 'days' unless defined? UNIT_DAYS
|
19
|
+
UNIT_WEEKS = 'weeks' unless defined? UNIT_WEEKS
|
20
|
+
UNIT_MONTHS = 'months' unless defined? UNIT_MONTHS
|
21
|
+
ALL_UNITS = [UNIT_HOURS, UNIT_DAYS, UNIT_WEEKS, UNIT_MONTHS].freeze unless defined? ALL_UNITS
|
22
|
+
#
|
23
|
+
# fields
|
24
|
+
#
|
25
|
+
field :expire_at, type: Integer
|
26
|
+
field :expiration_type, type: String, default: OPTION_KILL
|
27
|
+
field :expiration_message, type: String, default: 'The application has expired'
|
28
|
+
field :expiration_time_zone, type: String, default: 'local'
|
29
|
+
# field :kill_after_hours, type: Integer
|
30
|
+
field :kill_after_interval, type: Integer
|
31
|
+
field :kill_after_unit, type: String
|
32
|
+
field :wipe_after_interval, type: Integer
|
33
|
+
field :wipe_after_unit, type: String
|
34
|
+
# field :wipe_after_hours, type: Integer
|
35
|
+
field :inactivity_message, type: String, default: 'The application has been inactive for too long a period'
|
36
|
+
field :start_time, type: String
|
37
|
+
field :end_time, type: String
|
38
|
+
field :time_window_time_zone, type: String, default: 'local'
|
39
|
+
field :days, type: String, default: 'MTWHFSU'
|
40
|
+
field :time_window_message, type: String, default: 'The application is trying to run outside of the allowed time'
|
41
|
+
field :time_window_active, type: Boolean, default: false
|
42
|
+
field :expiration_active, type: Boolean, default: false
|
43
|
+
field :check_in_active, type: Boolean, default: false
|
44
|
+
|
45
|
+
#
|
46
|
+
# return the policy hash for this agent
|
47
|
+
#
|
48
|
+
def enforce_policy(agent)
|
49
|
+
policy = super.merge(enforce_time_bomb: 0)
|
50
|
+
return policy unless active?
|
51
|
+
|
52
|
+
time_bomb_policies = expiration_policy.merge(check_in_policy).merge(time_window_policy)
|
53
|
+
|
54
|
+
if time_bomb_policies.present?
|
55
|
+
policy[:time_bomb] = time_bomb_policies
|
56
|
+
policy[:enforce_time_bomb] = 1
|
57
|
+
end
|
58
|
+
|
59
|
+
policy
|
60
|
+
rescue StandardError => error
|
61
|
+
log_error("Unable to build time bomb policy for agent: #{agent.inspect} - #{inspect}", error)
|
62
|
+
# Return the default policy
|
63
|
+
{ enforce_time_bomb: 0 }
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Determine if we should enforce time of day policy
|
68
|
+
#
|
69
|
+
def time_window_policy
|
70
|
+
return {} unless time_window_active?
|
71
|
+
|
72
|
+
{
|
73
|
+
time_window:
|
74
|
+
{ start_time: start_time,
|
75
|
+
end_time: end_time,
|
76
|
+
time_zone: time_window_time_zone,
|
77
|
+
days: days,
|
78
|
+
message: time_window_message }
|
79
|
+
}
|
80
|
+
rescue StandardError => error
|
81
|
+
log_error("Unable to build time bomb time window policy: #{inspect}", error)
|
82
|
+
{}
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# determine if we should enforce the expire at policy
|
87
|
+
#
|
88
|
+
def expiration_policy
|
89
|
+
return {} unless expiration_active? && expire_at.present?
|
90
|
+
|
91
|
+
{
|
92
|
+
expire:
|
93
|
+
{
|
94
|
+
expiration_after: expire_at,
|
95
|
+
time_zone: expiration_time_zone,
|
96
|
+
type: expiration_type,
|
97
|
+
message: expiration_message
|
98
|
+
}
|
99
|
+
}
|
100
|
+
rescue StandardError => error
|
101
|
+
log_error("Unable to build time bomb expire at policy: #{inspect}", error)
|
102
|
+
{}
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Determine if the check in, or "Time bomb" policy should be enforced
|
107
|
+
#
|
108
|
+
def check_in_policy
|
109
|
+
return {} unless check_in_active
|
110
|
+
|
111
|
+
policy = { message: inactivity_message }
|
112
|
+
policy[:kill_after] = check_in_milliseconds(:kill) if kill_after_interval.present?
|
113
|
+
policy[:wipe_after] = check_in_milliseconds(:wipe) if wipe_after_interval.present?
|
114
|
+
{ check_in: policy }
|
115
|
+
rescue StandardError => error
|
116
|
+
log_error("Unable to build time bomb check in policy: #{inspect}", error)
|
117
|
+
{}
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Return the number of milli seconds between check ins
|
122
|
+
#
|
123
|
+
def check_in_milliseconds(type)
|
124
|
+
interval = send("#{type}_after_interval").to_i
|
125
|
+
case send "#{type}_after_unit"
|
126
|
+
when UNIT_HOURS
|
127
|
+
interval * HOURS_TO_MS
|
128
|
+
when UNIT_DAYS
|
129
|
+
24 * interval * HOURS_TO_MS
|
130
|
+
when UNIT_WEEKS
|
131
|
+
7 * 24 * interval * HOURS_TO_MS
|
132
|
+
when UNIT_MONTHS
|
133
|
+
30 * 24 * interval * HOURS_TO_MS
|
134
|
+
else
|
135
|
+
raise "Unknown unit: #{type}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/models/user.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# A user in the system is someone who has access to the enterprise app store
|
5
|
+
#
|
6
|
+
class User
|
7
|
+
include Mongoid::Document
|
8
|
+
include Mongoid::Timestamps
|
9
|
+
include App47EmailSendable
|
10
|
+
include App47Logger
|
11
|
+
include Searchable
|
12
|
+
#
|
13
|
+
# Fields
|
14
|
+
#
|
15
|
+
field :name, type: String
|
16
|
+
field :active, type: Boolean, default: true
|
17
|
+
field :pin_code, type: String
|
18
|
+
field :pin_code_locked, type: Boolean, default: false
|
19
|
+
#
|
20
|
+
# Relationships
|
21
|
+
#
|
22
|
+
belongs_to :account, inverse_of: :users
|
23
|
+
# has_and_belongs_to_many :groups, dependent: :nullify
|
24
|
+
has_and_belongs_to_many :allowed_apps, class_name: 'App', dependent: :nullify, inverse_of: :user_apps
|
25
|
+
has_and_belongs_to_many :test_apps, class_name: 'App', dependent: :nullify, inverse_of: :test_users
|
26
|
+
has_many :app_permissions, class_name: 'UserAppPermission', dependent: :destroy
|
27
|
+
has_many :devices, class_name: 'UserDevice'
|
28
|
+
|
29
|
+
#
|
30
|
+
# Return the unique set of application ids for this given user
|
31
|
+
#
|
32
|
+
def allowed_app_set
|
33
|
+
app_permissions.collect(&:app) + account.apps.distinct_all_users
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Find a user device by safely trying a number of approaches
|
38
|
+
# 1. By ID
|
39
|
+
# 2. By unique identifier
|
40
|
+
# 3. By app47 identifier
|
41
|
+
# 4. By token
|
42
|
+
#
|
43
|
+
def find_device_by(device_id)
|
44
|
+
raise 'Invalid ID' if device_id.blank?
|
45
|
+
|
46
|
+
devices.where(_id: device_id).first ||
|
47
|
+
devices.where(unique_identifier: device_id).first ||
|
48
|
+
devices.where(app47_identifier: device_id).first ||
|
49
|
+
devices.where(token: device_id).first
|
50
|
+
rescue StandardError
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|