Module: Rhoconnect

Extended by:
Rhoconnect
Included in:
Rhoconnect, Server
Defined in:
lib/rhoconnect.rb,
lib/rhoconnect/app.rb,
lib/rhoconnect/user.rb,
lib/rhoconnect/async.rb,
lib/rhoconnect/model.rb,
lib/rhoconnect/store.rb,
lib/rhoconnect/client.rb,
lib/rhoconnect/server.rb,
lib/rhoconnect/source.rb,
lib/rhoconnect/license.rb,
lib/rhoconnect/version.rb,
lib/rhoconnect/api_token.rb,
lib/rhoconnect/credential.rb,
lib/rhoconnect/ping/apple.rb,
lib/rhoconnect/read_state.rb,
lib/rhoconnect/client_sync.rb,
lib/rhoconnect/source_sync.rb,
lib/rhoconnect/ping/android.rb,
lib/rhoconnect/stats/record.rb,
lib/rhoconnect/test_methods.rb,
lib/rhoconnect/jobs/ping_job.rb,
lib/rhoconnect/source_adapter.rb,
lib/rhoconnect/dynamic_adapter.rb,
lib/rhoconnect/jobs/source_job.rb,
lib/rhoconnect/ping/blackberry.rb,
lib/rhoconnect/middleware/stats.rb,
lib/rhoconnect/jobs/bulk_data_job.rb,
lib/rhoconnect/middleware/helpers.rb,
lib/rhoconnect/bulk_data/bulk_data.rb,
lib/rhoconnect/ping/rhoconnect_push.rb,
lib/rhoconnect/middleware/admin_user.rb,
lib/rhoconnect/middleware/current_app.rb,
lib/rhoconnect/rho_indifferent_access.rb,
lib/rhoconnect/middleware/current_user.rb,
lib/rhoconnect/middleware/login_required.rb,
lib/rhoconnect/middleware/current_request.rb,
lib/rhoconnect/predefined_adapters/bench_adapter.rb,
lib/rhoconnect/middleware/body_content_type_parser.rb,
lib/rhoconnect/middleware/x_domain_session_wrapper.rb

Overview

This class has dubious semantics and we only have it so that people can write params instead of params and they get the same value for both keys.

Defined Under Namespace

Modules: AsyncHelpers, BulkDataJob, Middleware, PingJob, Synchrony, TestMethods Classes: Android, ApiException, ApiToken, App, Apple, Base, Blackberry, BulkData, Client, ClientSync, Credential, DynamicAdapter, InvalidArgumentError, InvalidSourceNameError, Iphone, License, LicenseException, LicenseSeatsExceededException, LoginException, MemoryModel, Model, ReadState, RhoHashWithIndifferentAccess, RhoInternalBenchmarkAdapter, RhoconnectPush, RhoconnectServerError, Server, Source, SourceAdapter, SourceAdapterException, SourceAdapterLoginException, SourceAdapterLogoffException, SourceAdapterObjectConflictError, SourceAdapterServerErrorException, SourceAdapterServerTimeoutException, SourceJob, SourceSync, Stats, Store, StoreLockException, User

Constant Summary collapse

APP_NAME =
'application'
API_VERSION =
'v1'.freeze
VERSION =
'3.3.2'
CURRENT_REQUEST =

Constants

'CURRENT_REQUEST'.freeze
CURRENT_APP =
'CURRENT_APP'.freeze
CURRENT_USER =
'CURRENT_USER'.freeze
API_TOKEN_HEADER =

header names, in the form of server’s HTTP variables X-RhoConnect-API-TOKEN

'HTTP_X_RHOCONNECT_API_TOKEN'.freeze
CLIENT_ID_HEADER =

X-RhoConnect-CLIENT-ID

'HTTP_X_RHOCONNECT_CLIENT_ID'.freeze
OLD_API_URI_REGEXP =

old-way admin routes

/\/api\//
SYSTEM_API_URI_REGEXP =

new way system routes will be: /rc/<version>/system/<method> /rc/<version>/<resource>/<method>

/\/rc\/\w+\/\w+/
PUSH_API_URI_REGEXP =

also, /app/v1/<Controller>/push_* are still remain Admin routes

/\/app\/\w+\/\w+\/push\w+/
FAST_CUD_API_URI_REGEXP =

also, /app/v1/<Controller>/fast_* are still remain Admin routes

/\/app\/\w+\/\w+\/fast\w+/

Class Attribute Summary collapse

Instance Method Summary collapse

Class Attribute Details

.api_tokenObject

Returns the value of attribute api_token.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def api_token
  @api_token
end

.app_directoryObject

Returns the value of attribute app_directory.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def app_directory
  @app_directory
end

.appserverObject

Returns the value of attribute appserver.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def appserver
  @appserver
end

.base_directoryObject

Returns the value of attribute base_directory.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def base_directory
  @base_directory
end

.blackberry_bulk_syncObject

Returns the value of attribute blackberry_bulk_sync.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def blackberry_bulk_sync
  @blackberry_bulk_sync
end

.bulk_sync_poll_intervalObject

Returns the value of attribute bulk_sync_poll_interval.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def bulk_sync_poll_interval
  @bulk_sync_poll_interval
end

Returns the value of attribute cookie_expire.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def cookie_expire
  @cookie_expire
end

.data_directoryObject

Returns the value of attribute data_directory.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def data_directory
  @data_directory
end

.environmentObject

Returns the value of attribute environment.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def environment
  @environment
end

.licenseObject

Returns the value of attribute license.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def license
  @license
end

.lock_durationObject

Returns the value of attribute lock_duration.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def lock_duration
  @lock_duration
end

.log_disabledObject

Returns the value of attribute log_disabled.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def log_disabled
  @log_disabled
end

.predefined_sourcesObject

Returns the value of attribute predefined_sources.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def predefined_sources
  @predefined_sources
end

.raise_on_expired_lockObject

Returns the value of attribute raise_on_expired_lock.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def raise_on_expired_lock
  @raise_on_expired_lock
end

.redisObject

Returns the value of attribute redis.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def redis
  @redis
end

.statsObject

Returns the value of attribute stats.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def stats
  @stats
end

.vendor_directoryObject

Returns the value of attribute vendor_directory.



48
49
50
# File 'lib/rhoconnect.rb', line 48

def vendor_directory
  @vendor_directory
end

Instance Method Details

#bootstrap(basedir) {|_self| ... } ⇒ Object

Begin Rhoconnect setup methods

Server hook to initialize Rhoconnect

Yields:

  • (_self)

Yield Parameters:

  • _self (Rhoconnect)

    the object that the method was called on



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rhoconnect.rb', line 56

def bootstrap(basedir)
  config = get_config(basedir)
  #Load environment
  environment = (ENV['RHO_ENV'] || ENV['RACK_ENV'] || :development).to_sym  
  # Initialize Rhoconnect and Resque
  Rhoconnect.base_directory = basedir
  Rhoconnect.app_directory = get_setting(config,environment,:app_directory)
  Rhoconnect.data_directory = get_setting(config,environment,:data_directory)
  Rhoconnect.vendor_directory = get_setting(config,environment,:vendor_directory)
  Rhoconnect.blackberry_bulk_sync = get_setting(config,environment,:blackberry_bulk_sync,false)
  Rhoconnect.bulk_sync_poll_interval = get_setting(config,environment,:bulk_sync_poll_interval,3600)
  Rhoconnect.redis = get_setting(config,environment,:redis,false)
  Rhoconnect.api_token = ENV['API_TOKEN'] || get_setting(config,environment,:api_token,false)
  Rhoconnect.log_disabled = get_setting(config,environment,:log_disabled,false)
  Rhoconnect.raise_on_expired_lock = get_setting(config,environment,:raise_on_expired_lock,false)
  Rhoconnect.lock_duration = get_setting(config,environment,:lock_duration)
  Rhoconnect.environment = environment
  Rhoconnect.cookie_expire = get_setting(config,environment,:cookie_expire)  || 31536000
  Rhoconnect.predefined_sources = {}
  yield self if block_given?
  Store.create(Rhoconnect.redis)
  Resque.redis = Store.db
  Rhoconnect.base_directory ||= File.join(File.dirname(__FILE__),'..')
  Rhoconnect.app_directory ||= Rhoconnect.base_directory
  Rhoconnect.data_directory ||= File.join(Rhoconnect.base_directory,'data')
  Rhoconnect.vendor_directory ||= File.join(Rhoconnect.base_directory,'vendor')
  Rhoconnect.stats ||= false
  Rhoconnect.license = License.new
  
  check_and_add(File.join(Rhoconnect.app_directory,'sources'))
  start_app(config)
  create_admin_user
  check_hsql_lib! if Rhoconnect.blackberry_bulk_sync
end

#camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true) ⇒ Object

Taken from rails inflector



236
237
238
239
240
# File 'lib/rhoconnect.rb', line 236

def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
  if first_letter_in_uppercase
    lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
  end
end

#check_and_add(path) ⇒ Object

Add path to load_path unless it has been added already



131
132
133
# File 'lib/rhoconnect.rb', line 131

def check_and_add(path)
  $:.unshift path unless $:.include?(path) 
end

#check_default_secret!(secret) ⇒ Object



179
180
181
182
183
184
185
186
# File 'lib/rhoconnect.rb', line 179

def check_default_secret!(secret)
  if secret == '<changeme>'                        
    log "*"*60+"\n\n"
    log "WARNING: Change the session secret in config.ru from <changeme> to something secure."
    log "  i.e. running `rake rhoconnect:secret` in your rhoconnect app directory will generate a secret you could use.\n\n"
    log "*"*60
  end
end

#check_for_schema_field!(fields) ⇒ Object



188
189
190
191
192
193
# File 'lib/rhoconnect.rb', line 188

def check_for_schema_field!(fields)
  if fields['schema']
    log "ERROR: 'schema' field in settings.yml is not supported anymore, please use source adapter schema method!"
    exit(1)
  end
end

#check_hsql_lib!Object



242
243
244
245
246
247
248
249
250
# File 'lib/rhoconnect.rb', line 242

def check_hsql_lib!
  unless File.exists?(File.join(Rhoconnect.vendor_directory,'hsqldata.jar'))
    log "*"*60
    log ""
    log "WARNING: Missing vendor/hsqldata.jar, please install it for BlackBerry bulk sync support."
    log ""
    log "*"*60
  end
end

#compute_token(doc_key) ⇒ Object

Computes token for a single client request



220
221
222
223
224
# File 'lib/rhoconnect.rb', line 220

def compute_token(doc_key)
  token = get_token
  Store.put_value(doc_key,token)
  token.to_s
end

#create_admin_userObject

Generate admin user on first load



122
123
124
125
126
127
128
# File 'lib/rhoconnect.rb', line 122

def create_admin_user
  unless User.is_exist?('rhoadmin')
    admin = User.create({:login => 'rhoadmin', :admin => 1})
    admin.password = ENV['PSWRD'] || ''
    admin.create_token
  end
end

#create_predefined_source(source_name, params) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/rhoconnect.rb', line 166

def create_predefined_source(source_name,params)
  source_data = Rhoconnect.predefined_sources[source_name]
  return unless source_data
  if source_data[:source_loaded] != true
    source_config = Rhoconnect.source_config(source_name)
    source_config[:name] = source_name
    Source.create(source_config,params)
    app = App.load(Rhoconnect::APP_NAME)
    app.sources << source_name
    source_data[:source_loaded] = true
  end
end

#expire_bulk_data(username, partition = :user) ⇒ Object



252
253
254
255
256
# File 'lib/rhoconnect.rb', line 252

def expire_bulk_data(username, partition = :user)
  name = BulkData.get_name(partition,username)
  data = BulkData.load(name)
  data.refresh_time = Time.now.to_i if data
end

#extract_api_user(env) ⇒ Object



97
98
99
100
101
102
103
104
# File 'lib/rhoconnect/middleware/helpers.rb', line 97

def extract_api_user(env)
  r = env[CURRENT_REQUEST]
  # API_TOKEN is either a param or a header
  u = ApiToken.load(env[API_TOKEN_HEADER])
  u = ApiToken.load(r.params['api_token']) unless u
  raise Rhoconnect::ApiException.new(422, "No API token provided") unless u
  u.user 
end

#extract_current_user(env) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rhoconnect/middleware/helpers.rb', line 85

def extract_current_user(env)
  user = nil
  if User.is_exist?(env['rack.session'][:login])
    user = User.load(env['rack.session'][:login])
  end
  if user and (user.admin == 1 || env['rack.session'][:app_name] == APP_NAME)
    user
  else  
    nil
  end
end

#get_config(basedir) ⇒ Object



135
136
137
138
139
# File 'lib/rhoconnect.rb', line 135

def get_config(basedir)
  # Load settings
  settings_file = File.join(basedir,'settings','settings.yml') if basedir
  YAML.load_file(settings_file) if settings_file and File.exist?(settings_file)
end

#get_random_uuidObject

Get random UUID string



209
210
211
# File 'lib/rhoconnect.rb', line 209

def get_random_uuid
  UUIDTools::UUID.random_create.to_s.gsub(/\-/,'')
end

#get_tokenObject

Generates new token (64-bit integer) based on # of microseconds since Jan 1 2009



215
216
217
# File 'lib/rhoconnect.rb', line 215

def get_token
  ((Time.now.to_f - Time.mktime(2009,"jan",1,0,0,0,0).to_f) * 10**6).to_i
end

#getelement(element) ⇒ Object

De-serializes oav from set element



202
203
204
205
206
# File 'lib/rhoconnect.rb', line 202

def getelement(element)
  res = element.split(':',3)
  #[res[0], res[1], Base64.decode64(res[2].to_s)]
  [res[0], res[1], res[2]]
end

#is_admin_route(env) ⇒ Object



60
61
62
# File 'lib/rhoconnect/middleware/helpers.rb', line 60

def is_admin_route(env)
  not is_client_route(env) 
end

#is_client_route(env) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/rhoconnect/middleware/helpers.rb', line 32

def is_client_route(env)
  # FIXME - once Controllers concept is implemented
  # remove this - since the inclusion of Admin Middleware
  # will automatically imply what routes are Admin and which are not
  full_path = env['SCRIPT_NAME'] + env['PATH_INFO']
  # login route and index route doesn't need a token
  return true if /\/login/.match(full_path) || full_path == '/'
  # old application routes are not admin 
  return true if full_path == '/application'
  return true if full_path == '/api/application'
  return true if /\/application\//.match(full_path)
  # some of the clients routes are not admin
  return true if is_clients_non_admin_route(env)
  # push_objects and push_deletes methods are admin
  return false if PUSH_API_URI_REGEXP.match(full_path)
  # fast_insert/update/delete methods are admin
  return false if FAST_CUD_API_URI_REGEXP.match(full_path) 
  # old api methods + every method in /rc/v1/app namespace
  return true if /\/app\//.match(full_path)
  # all other methods in the /rc/v1/ are admin routes
  return false if SYSTEM_API_URI_REGEXP.match(full_path)
  # all old /api methods are admin as well
  return false if OLD_API_URI_REGEXP.match(full_path)
  
  # everything else should not be admin
  true
end

#is_clients_non_admin_route(env) ⇒ Object



25
26
27
28
29
30
# File 'lib/rhoconnect/middleware/helpers.rb', line 25

def is_clients_non_admin_route(env)
  # FIXME : not all routes for Clients protocol are admin routes
  full_path = env['SCRIPT_NAME'] + env['PATH_INFO']
  /\/rc\/\w+\/clients/.match(full_path) and not ((env[CURRENT_REQUEST].request_method == 'GET') ||
    (env[CURRENT_REQUEST].request_method == 'POST' and /\/rc\/\w+\/clients\/\w+\/sources/.match(full_path)))
end

#is_login_required(env) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rhoconnect/middleware/helpers.rb', line 64

def (env)
  # FIXME - once Controllers concept is implemented
  # remove this - since the inclusion of LoginRequired Middleware
  # will automatically imply what routes require Login and which are not
  full_path = env['SCRIPT_NAME'] + env['PATH_INFO']
  return false if full_path == '/' or /login/.match(full_path)
  # all methods in app namespace
  return true if  /\/app\//.match(full_path)
  # old application routes are not admin 
  return true if full_path == '/application'
  return true if full_path == '/api/application'
  return true if /\/application\//.match(full_path)
  # also methods that operate on clients should have an associated user
  return true if is_clients_non_admin_route(env)
  
  # for every other route login is not required
  # because it's either an admin route (where api_user must be supplied anyway)
  # or it's a custom route extending RhoConnect server
  false
end

#lap_timer(msg, start) ⇒ Object



279
280
281
282
283
# File 'lib/rhoconnect.rb', line 279

def lap_timer(msg,start)
  duration = timenow - start
  log "#{msg}: #{duration}"
  timenow
end

#log(*args) ⇒ Object



294
295
296
297
# File 'lib/rhoconnect.rb', line 294

def log(*args)
  now = Time.now.strftime('%I:%M:%S %p %Y-%m-%d')
  puts "[#{now}] #{args.join}" unless Rhoconnect.log_disabled
end

#register_predefined_source(source_name) ⇒ Object

End Rhoconnect setup methods



160
161
162
163
164
# File 'lib/rhoconnect.rb', line 160

def register_predefined_source(source_name)
  return if Rhoconnect.predefined_sources.has_key?(source_name)
  
  Rhoconnect.predefined_sources[source_name] = {:source_loaded => false}
end

#setelement(obj, attrib, value) ⇒ Object

Serializes oav to set element



196
197
198
199
# File 'lib/rhoconnect.rb', line 196

def setelement(obj,attrib,value)
  #"#{obj}:#{attrib}:#{Base64.encode64(value.to_s)}"
  "#{obj}:#{attrib}:#{value.to_s}"
end

#source_config(source_name) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/rhoconnect.rb', line 141

def source_config(source_name)
  source_config = {}
  sources = Rhoconnect.get_config(Rhoconnect.base_directory)[:sources]
  source_config = sources[source_name] unless (sources.nil? or sources[source_name].nil?)
  env_config = Rhoconnect.get_config(Rhoconnect.base_directory)[Rhoconnect.environment]
  force_default = source_config[:force_default]
  source_config.delete(:force_default)
  # apply global env settings
  [:poll_interval].each do |setting|
    def_setting = env_config["#{setting.to_s}_default".to_sym]
    next unless def_setting      
    if source_config[setting].nil? or force_default
      source_config[setting] = def_setting
    end
  end
  source_config
end

#start_app(config) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rhoconnect.rb', line 91

def start_app(config)
  if config and config[Rhoconnect.environment]
    app = nil
    app_name = APP_NAME
    if App.is_exist?(app_name)
      app = App.load(app_name)
    else
      app = App.create(:name => app_name)
    end
    sources = config[:sources] || []
    Source.delete_all
    app.delete_sources
    sources.each do |source_name,fields|
      source_config = source_config(source_name)
      check_for_schema_field!(source_config)
      source_config[:name] = source_name
      Source.create(source_config,{:app_id => app.name})
      app.sources << source_name
      # load ruby file for source adapter to re-load class
      load under_score(source_name+'.rb')
    end
    
    # load all pre-defined adapters files
    Dir[File.join(File.dirname(__FILE__),'rhoconnect','predefined_adapters','*.rb')].each { |adapter| load adapter }
    
    # Create associations for all sources
    Source.update_associations(app.sources)
  end
end

#start_timer(msg = 'starting') ⇒ Object



285
286
287
288
# File 'lib/rhoconnect.rb', line 285

def start_timer(msg='starting')
  log "#{msg}"
  timenow
end

#timenowObject



290
291
292
# File 'lib/rhoconnect.rb', line 290

def timenow
  (Time.now.to_f * 1000)
end

#under_score(camel_cased_word) ⇒ Object

Returns require-friendly filename for a class



227
228
229
230
231
232
233
# File 'lib/rhoconnect.rb', line 227

def under_score(camel_cased_word)
  camel_cased_word.to_s.gsub(/::/, '/').
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
  tr("-", "_").
  downcase
end

#unzip_file(file_dir, params) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/rhoconnect.rb', line 258

def unzip_file(file_dir,params)
  uploaded_file = File.join(file_dir, params[:filename])
  begin
    File.open(uploaded_file, 'wb') do |file|
      file.write(params[:tempfile].read)
    end
    Zip::ZipFile.open(uploaded_file) do |zip_file|
      zip_file.each do |f|
        f_path = File.join(file_dir,f.name)
        FileUtils.mkdir_p(File.dirname(f_path))
        zip_file.extract(f, f_path) { true }
      end
    end
  rescue Exception => e
    log "Failed to unzip `#{uploaded_file}`"
    raise e
  ensure
    FileUtils.rm_f(uploaded_file)
  end
end