rapid 0.0.1

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