rapid 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/Rakefile +66 -0
  2. data/lib/rad/http_controller/acts_as/authenticated.rb +131 -0
  3. data/lib/rad/http_controller/acts_as/authenticated_master_domain.rb +119 -0
  4. data/lib/rad/http_controller/acts_as/authorized.rb +83 -0
  5. data/lib/rad/http_controller/acts_as/localized.rb +27 -0
  6. data/lib/rad/http_controller/acts_as/multitenant.rb +53 -0
  7. data/lib/rad/http_controller/helpers/service_mix_helper.rb +50 -0
  8. data/lib/rad/http_controller.rb +15 -0
  9. data/lib/rad/lib/text_utils.rb +334 -0
  10. data/lib/rad/locales/en.yml +80 -0
  11. data/lib/rad/locales/ru.yml +83 -0
  12. data/lib/rad/locales.rb +2 -0
  13. data/lib/rad/models/account.rb +88 -0
  14. data/lib/rad/models/default_permissions.yml +26 -0
  15. data/lib/rad/models/micelaneous.rb +1 -0
  16. data/lib/rad/models/role.rb +88 -0
  17. data/lib/rad/models/secure_token.rb +33 -0
  18. data/lib/rad/models/space.rb +184 -0
  19. data/lib/rad/models/user.rb +158 -0
  20. data/lib/rad/models.rb +41 -0
  21. data/lib/rad/mongo_mapper/acts_as/authenticated_by_open_id.rb +29 -0
  22. data/lib/rad/mongo_mapper/acts_as/authenticated_by_password.rb +120 -0
  23. data/lib/rad/mongo_mapper/acts_as/authorized.rb +197 -0
  24. data/lib/rad/mongo_mapper/acts_as/authorized_object.rb +171 -0
  25. data/lib/rad/mongo_mapper/multitenant.rb +34 -0
  26. data/lib/rad/mongo_mapper/rad_micelaneous.rb +43 -0
  27. data/lib/rad/mongo_mapper/space_keys.rb +62 -0
  28. data/lib/rad/mongo_mapper/text_processor.rb +47 -0
  29. data/lib/rad/mongo_mapper.rb +20 -0
  30. data/lib/rad/paperclip/callbacks.rb +40 -0
  31. data/lib/rad/paperclip/extensions.rb +64 -0
  32. data/lib/rad/paperclip/integration.rb +165 -0
  33. data/lib/rad/paperclip/mime.rb +5 -0
  34. data/lib/rad/paperclip/validations.rb +64 -0
  35. data/lib/rad/paperclip.rb +11 -0
  36. data/lib/rad/spec/controller.rb +51 -0
  37. data/lib/rad/spec/model/factories.rb +65 -0
  38. data/lib/rad/spec/model.rb +85 -0
  39. data/lib/rad/spec/rem_helper.rb +145 -0
  40. data/lib/rad/spec.rb +4 -0
  41. data/lib/rad/tasks/backup.rake +64 -0
  42. data/lib/rad/tasks/initialize.rake +35 -0
  43. data/lib/rad.rb +32 -0
  44. data/readme.md +3 -0
  45. data/spec/controller/authorization_spec.rb +146 -0
  46. data/spec/controller/helper.rb +14 -0
  47. data/spec/lib/helper.rb +7 -0
  48. data/spec/lib/text_utils_spec.rb +238 -0
  49. data/spec/models/authorization_spec.rb +93 -0
  50. data/spec/models/authorized_object_spec.rb +258 -0
  51. data/spec/models/file_audit_spec/100.txt +1 -0
  52. data/spec/models/file_audit_spec/302.txt +3 -0
  53. data/spec/models/file_audit_spec.rb +168 -0
  54. data/spec/models/helper.rb +11 -0
  55. data/spec/models/space_key_spec.rb +68 -0
  56. data/spec/models/user_spec.rb +80 -0
  57. data/spec/mongo_mapper/basic_spec.rb +41 -0
  58. data/spec/mongo_mapper/helper.rb +10 -0
  59. data/spec/spec.opts +4 -0
  60. metadata +138 -0
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ Dir.chdir File.dirname(__FILE__)
5
+
6
+ # Specs
7
+ task :default => :spec
8
+
9
+ Spec::Rake::SpecTask.new('spec') do |t|
10
+ t.spec_files = FileList["spec/**/*_spec.rb"].select{|f| f !~ /\/_/}
11
+ t.libs = ['lib'].collect{|f| "#{File.dirname __FILE__}/#{f}"}
12
+ end
13
+
14
+ # Gem
15
+ require 'rake/clean'
16
+ require 'rake/gempackagetask'
17
+ require 'fileutils'
18
+
19
+ spec = Gem::Specification.new do |s|
20
+ s.name = "rapid"
21
+ s.version = "0.0.1"
22
+ s.summary = "Rapid Application Development platform for Crystal framework"
23
+ s.description = "Rapid Application Development platform for Crystal framework"
24
+ s.author = "Alexey Petrushin"
25
+ # s.email = ""
26
+ s.homepage = "http://github.com/alexeypetrushin/rapid"
27
+
28
+ s.platform = Gem::Platform::RUBY
29
+ s.has_rdoc = true
30
+ # s.extra_rdoc_files = ["README.rdoc"]
31
+
32
+ # s.files = (%w{rakefile readme.md .gitignore} + Dir.glob("{app,lib,spec,.git}/**/*"))
33
+ s.files = (['Rakefile', 'readme.md'] + Dir.glob("{lib,spec}/**/*"))
34
+
35
+ s.require_paths = ["lib"]
36
+
37
+ [
38
+ 'crystal',
39
+ ].each{|name| s.add_dependency(name)}
40
+
41
+ end
42
+
43
+ PACKAGE_DIR = "#{File.expand_path File.dirname(__FILE__)}/build"
44
+
45
+ Rake::GemPackageTask.new(spec) do |p|
46
+ package_dir = PACKAGE_DIR
47
+ # FileUtils.mkdir package_dir unless File.exist? package_dir
48
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
49
+ p.need_zip = true
50
+ p.package_dir = package_dir
51
+ end
52
+
53
+ # CLEAN.include [ 'pkg', '*.gem']
54
+
55
+ task :push do
56
+ dir = Dir.chdir PACKAGE_DIR do
57
+ gem_file = Dir.glob("rapid*.gem").first
58
+ system "gem push #{gem_file}"
59
+ end
60
+ end
61
+
62
+ task :clean do
63
+ system "rm -r #{PACKAGE_DIR}"
64
+ end
65
+
66
+ task :release => [:gem, :push, :clean]
@@ -0,0 +1,131 @@
1
+ # require 'rack/auth/basic.rb'
2
+
3
+ module Crystal
4
+ module HttpController
5
+ module Authenticated
6
+
7
+ module ClassMethods
8
+ def acts_as_authenticated
9
+ include Rad::Authenticated::InstanceMethods
10
+
11
+ # TODO2 what to do whith helper methods?
12
+ # helper_method :login_path, :logout_path, :signup_path, :user_path
13
+
14
+ before :prepare_current_user_for_slave_domain
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ protected
20
+ def prepare_current_user_for_slave_domain
21
+ unless check_and_execute_cas_command
22
+ user = login_from_basic_auth || login_from_session || login_as_anonymous
23
+ raise "You probably don't create Anonymous User!" if user.nil?
24
+ User.current = user
25
+ end
26
+ end
27
+
28
+
29
+ #
30
+ # Authentication Methods
31
+ #
32
+ def login_from_basic_auth
33
+ # TODO2
34
+ # authenticate_with_http_basic do |login, password|
35
+ # User.authenticate_by_password login, password unless login.blank? or password.blank?
36
+ # end
37
+ # username, password = workspace.request.credentials
38
+ # User.authenticate_by_password username, password unless username.blank? or password.blank?
39
+ end
40
+
41
+ def login_from_session
42
+ session = workspace.request.session
43
+ User.find_by_id session['user_id'] unless session['user_id'].blank?
44
+ end
45
+
46
+ def login_as_anonymous
47
+ workspace.request.session[:user_id] = User.anonymous.id.to_s
48
+ User.anonymous
49
+ end
50
+
51
+
52
+ #
53
+ # CAS Authentication
54
+ #
55
+ def check_and_execute_cas_command
56
+ return unless params.include?('cas_token') or params.include?('cas_logout')
57
+
58
+ session = workspace.request.session
59
+ if params.include?('cas_token')
60
+ token = !params[:cas_token].blank? && SecureToken.by_token(params[:cas_token])
61
+ clear_session!
62
+ if token and !token[:user_id].blank? and (user = User.first(:id => token[:user_id], :state => 'active'))
63
+ session[:user_id] = user.id.to_s
64
+ else
65
+ session[:user_id] = User.anonymous.id.to_s
66
+ flash[:sticky_info] = t(:cas_try_more)
67
+ end
68
+ elsif params.include? 'cas_logout'
69
+ clear_session!
70
+ login_as_anonymous
71
+ end
72
+
73
+ # redirect to remove CAS params
74
+ uri = Uri.parse request.url
75
+ values = uri.query_values || {}
76
+ values.delete 'cas_logout'
77
+ values.delete 'cas_token'
78
+ uri.query_values = values
79
+
80
+ redirect_to uri.to_s
81
+ end
82
+
83
+
84
+ #
85
+ # Helpers
86
+ #
87
+ %w{login logout signup}.each do |path|
88
+ define_method "#{path}_path" do |*args|
89
+ options = args.first || {}
90
+ options = {
91
+ :host => config.master_domain!,
92
+ :port => config.port(nil),
93
+ :l => I18n.locale
94
+ }.merge(options)
95
+ options[:_return_to] = request.url unless params.include? :_return_to
96
+ url_for "#{ServiceMix.relative_url_root}/#{path}", options
97
+ end
98
+ end
99
+
100
+ def user_path user, options = {}
101
+ url_for_path "#{ServiceMix.relative_url_root}/users/#{user.to_param}", options
102
+ end
103
+
104
+
105
+ #
106
+ # Special
107
+ #
108
+ PRESERVE_SESSION_KEYS = %w{authenticity_token}
109
+ crystal.after :config do |config|
110
+ if config.session?
111
+ session_id = config.session!.key!
112
+ PRESERVE_SESSION_KEYS << session_id unless PRESERVE_SESSION_KEYS.include? session_id
113
+ end
114
+ end
115
+
116
+
117
+ def clear_session!
118
+ workspace.request.session
119
+
120
+ session[:dumb_key] # hack, need this to initialize session, othervise it's empty
121
+ to_delete = session.keys.select{|key| !PRESERVE_SESSION_KEYS.include?(key.to_s)}
122
+ to_delete.each{|key| session.delete key}
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+ end
129
+ end
130
+
131
+ Crystal::HttpController.inherit Crystal::HttpController::Authenticated
@@ -0,0 +1,119 @@
1
+ module Crystal
2
+ module HttpController
3
+ module AuthenticatedMasterDomain
4
+
5
+ module ClassMethods
6
+ def acts_as_authenticated_master_domain
7
+ include Crystal::HttpController::AuthenticatedMasterDomain::InstanceMethods
8
+
9
+ # TODO1 what to do with helper_methods?
10
+ # helper_method :login_path, :logout_path, :signup_path, :user_path
11
+ before :prepare_current_user_for_master_domain
12
+ end
13
+ end
14
+
15
+ module InstanceMethods
16
+ include ::Crystal::HttpController::Authenticated::InstanceMethods
17
+
18
+ protected
19
+ def prepare_current_user_for_master_domain
20
+ user = login_from_basic_auth || login_from_session || login_from_cookie || login_as_anonymous
21
+ raise "You probably don't create Anonymous User!" if user.nil?
22
+ User.current = user
23
+ end
24
+
25
+
26
+ #
27
+ # Authentication Methods
28
+ #
29
+ def login_from_cookie
30
+ session, cookies = workspace.request.session, workspace.request.cookies
31
+
32
+ token = !cookies[:auth_token].blank? && SecureToken.by_token(cookies[:auth_token])
33
+ if token and !token[:user_id].blank? and (user = User.first(:id => token[:user_id], :state => 'active'))
34
+ session[:user_id] = user.id.to_s
35
+ user
36
+ end
37
+ end
38
+
39
+
40
+ #
41
+ # CAS Authentication
42
+ #
43
+
44
+ # returns cas_token only for another domains
45
+ def return_to_path_with_cas_token
46
+ unless master_domain? params[:_return_to]
47
+ token = SecureToken.new
48
+ token.expires_at = 5.minutes.from_now
49
+ token[:type] = 'cas'
50
+ token[:user_id] = User.current.id.to_s
51
+ token.save!
52
+
53
+ return return_to_path(:cas_token => token.token)
54
+ end
55
+
56
+ return_to_path
57
+ end
58
+
59
+ def return_to_path_with_logout_cas_token
60
+ unless master_domain? params[:_return_to]
61
+ return_to_path(:cas_logout => 'true')
62
+ else
63
+ return_to_path
64
+ end
65
+ end
66
+
67
+ def return_cas_token_if_authenticated
68
+ # TODO1 fix it, there's no redirect anymore
69
+ redirect_to return_to_path_with_cas_token unless User.current.anonymous?
70
+ end
71
+
72
+
73
+ #
74
+ # Special
75
+ #
76
+ def master_domain? uri
77
+ unless uri.blank?
78
+ uri = Uri.parse(uri)
79
+
80
+ if !uri.host.blank? and uri.normalized_host != SETTING.master_domain!
81
+ return false
82
+ end
83
+ end
84
+ true
85
+ end
86
+
87
+ def set_current_user_with_updating_session user
88
+ session, cookies = workspace.request.session, workspace.request.cookies
89
+ current_user = User.current
90
+ user.must_not == current_user
91
+
92
+ # Clear
93
+ clear_session!
94
+ unless current_user.anonymous?
95
+ SecureToken.delete_all :user_id => current_user.id.to_s
96
+ cookies.delete :auth_token
97
+ end
98
+
99
+ # Set session and cookie token
100
+ session[:user_id] = user.id.to_s
101
+ unless user.anonymous?
102
+ token = SecureToken.new
103
+ token[:user_id] = user.id.to_s
104
+ token[:type] = 'cookie_auth'
105
+ token.expires_at = 2.weeks.from_now
106
+ token.save!
107
+
108
+ cookies[:auth_token] = {:value => token.token, :expires => token.expires_at}
109
+ end
110
+
111
+ User.current = user
112
+ end
113
+ end
114
+
115
+ end
116
+ end
117
+ end
118
+
119
+ Crystal::HttpController.inherit Crystal::HttpController::AuthenticatedMasterDomain
@@ -0,0 +1,83 @@
1
+ module Crystal
2
+ module HttpController
3
+ module Authorized
4
+
5
+ module ClassMethods
6
+ def acts_as_authorized
7
+ include Crystal::HttpController::Authorized::InstanceMethods
8
+ extend Crystal::HttpController::Authorized::SingletonMethods
9
+
10
+ # TODO1 uncomment thouse helper methods
11
+ # helper_method :can?, :owner?
12
+ end
13
+ end
14
+
15
+ module SingletonMethods
16
+ def require_permission operation, *args, &object_proc
17
+ operation = operation.must_be.a([String, Symbol]).to_s
18
+ # operation.should! :be_in, Space.permissions
19
+
20
+ options = args.extract_options!
21
+ # object_proc = args.size > 0 ? args.first : lambda{}
22
+ object_proc ||= lambda{}
23
+
24
+ method = "require_permission_#{operation}"
25
+ define_method method do
26
+ require_permission operation, instance_eval(&object_proc)
27
+ end
28
+ before method, options
29
+ end
30
+ # alias_method :require_permission_to, :require_permission
31
+ end
32
+
33
+ module InstanceMethods
34
+ protected
35
+ def can? *args
36
+ User.current.can? *args
37
+ end
38
+
39
+ def owner? *args
40
+ User.current.owner? *args
41
+ end
42
+
43
+ def login_required
44
+ access_denied! unless User.current.registered?
45
+ end
46
+
47
+ def login_not_required
48
+ raise_user_error t(:login_not_required) if User.current.registered?
49
+ end
50
+
51
+ def require_permission operation, object = nil
52
+ operation = operation.must_be.a([String, Symbol]).to_s
53
+ # operation.should! :be_in, Space.permissions
54
+
55
+ unless User.current.can? operation, object
56
+ crystal.logger.warn "Access denied, #{User.current.name} hasn't rights to #{operation}!"
57
+ access_denied!
58
+ end
59
+ end
60
+
61
+ def access_denied!
62
+ raise_user_error t(:access_denied)
63
+ end
64
+
65
+ # def access_denied
66
+ # respond_to do |format|
67
+ # format.html do
68
+ # flash[:info] = t(:login_required)
69
+ # session[:_return_to] = request.request_uri
70
+ # redirect_to new_session_path
71
+ # end
72
+ #
73
+ # format.any(:json, :xml) do
74
+ # request_http_basic_authentication 'Web Password'
75
+ # end
76
+ # end
77
+ # end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ Crystal::HttpController.inherit Crystal::HttpController::Authorized
@@ -0,0 +1,27 @@
1
+ module Crystal
2
+ module HttpController
3
+ module Localized
4
+
5
+ module ClassMethods
6
+ def acts_as_localized
7
+ include Crystal::HttpController::Localized::InstanceMethods
8
+ before :prepare_locale
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ protected
14
+ def prepare_locale
15
+ default_language = (Space.current? ? Space.current.language : nil) || config.default_language('en')
16
+ I18n.locale = params[:l] || default_language
17
+
18
+ # Delete l from params if language is the same as default
19
+ params.delete 'l' if params[:l] == default_language
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+ Crystal::HttpController.inherit Crystal::HttpController::Localized
@@ -0,0 +1,53 @@
1
+ module Crystal
2
+ module HttpController
3
+ module Multitenant
4
+
5
+ module InstanceMethods
6
+ protected
7
+ def select_account_and_space
8
+ begin
9
+ # preparing Account
10
+ subdomains = request.subdomains.select{|n| n != 'www'}
11
+ subdomain = subdomains.last
12
+ pure_domain = request.domain.sub(/\A#{subdomains.join(".")}\./, "")
13
+
14
+ domain = subdomain.blank? ? pure_domain : "#{subdomain}.#{pure_domain}"
15
+
16
+ Account.current = Account.first :conditions => {:domains => domain}
17
+ unless Account.current?
18
+ msg = "No Account registered for the '#{pure_domain}' Domain"
19
+ logger.debug msg
20
+ raise msg
21
+ end
22
+
23
+ # preparing Space
24
+ space_name = params[:s] || 'default'
25
+ Space.current = Account.current.spaces.first :name => space_name
26
+
27
+ unless Space.current?
28
+ msg = "No '#{space_name}' Space for '#{Account.current.name}' Account"
29
+ logger.debug msg
30
+ raise msg
31
+ end
32
+
33
+ yield
34
+ ensure
35
+ Account.current = nil
36
+ Space.current = nil
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ module ClassMethods
43
+ def acts_as_multitenant
44
+ include Rad::Multitenant::InstanceMethods
45
+
46
+ around :select_account_and_space
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ Crystal::HttpController.inherit Crystal::HttpController::Multitenant
@@ -0,0 +1,50 @@
1
+ module ServiceMixHelper
2
+
3
+ # [
4
+ # ['BOS-Tec', '/'],
5
+ # ['Lab', lab_path]
6
+ # ]
7
+ #
8
+ def logo
9
+ return [] if @logo.blank?
10
+ logo = @logo.is_a?(Array) ? @logo : [@logo]
11
+ logo.size.must_be.in 1..2
12
+
13
+ # Delete default space name if there is
14
+ logo = logo.select do |item|
15
+ if item.is_a?(Array)
16
+ !Space.default?(item[0])
17
+ else
18
+ !Space.default?(item)
19
+ end
20
+ end
21
+
22
+ logo.collect{|item| item.is_a?(Array) ? link_to(item.first, item.last) : item}
23
+ end
24
+
25
+ # # [
26
+ # # ['Users', user_path],
27
+ # # ['Roles']
28
+ # # ]
29
+ # #
30
+ # def breadcrumb
31
+ # Array(@breadcrumb)
32
+ # end
33
+ #
34
+
35
+ # def visibility_selector_for object
36
+ # object.should_not! :be_nil
37
+ # object.should! :be_a, MongoMapper::Acts::AuthorizedObject
38
+ # field_name = "#{object.class.model_name.underscore}[visibility_as_string]"
39
+ # html = ""
40
+ # Role::VISIBILITY_ROLES.each do |role|
41
+ # checked = role == object.visibility_as_string
42
+ # html += radio_button_tag field_name, role, checked
43
+ # html += label_tag "#{field_name}_#{role}", t("#{role}_role")
44
+ # html += "\n"
45
+ # end
46
+ # html
47
+ # end
48
+ end
49
+
50
+ Crystal::HttpController.helper ServiceMixHelper
@@ -0,0 +1,15 @@
1
+ %w(
2
+ authenticated
3
+ authenticated_master_domain
4
+ authorized
5
+ localized
6
+ multitenant
7
+ ).each{|n| require "rad/http_controller/acts_as/#{n}"}
8
+
9
+ require 'rad/http_controller/helpers/service_mix_helper'
10
+
11
+ require 'rad/locales'
12
+
13
+ Crystal::Config::DEFAULTS.merge!({
14
+ :master_domain => "localhost",
15
+ }.stringify_keys)