ixtlan 0.2.4 → 0.3.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.
- data/History.txt +49 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +73 -48
- data/Rakefile +1 -1
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/gwt_ixtlan_datamapper_rspec_scaffold_generator.rb +3 -3
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/AbstractApplicationResourceTestGwt.java +1 -1
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/Fields.java +1 -1
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/Model.java +2 -2
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/ModelFactory.java +2 -2
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/Screen.java +2 -2
- data/generators/gwt_ixtlan_datamapper_rspec_scaffold/templates/TestGwt.java +2 -2
- data/generators/ixtlan_datamapper_rspec_model/ixtlan_datamapper_rspec_model_generator.rb +1 -1
- data/generators/ixtlan_datamapper_rspec_scaffold/ixtlan_datamapper_rspec_scaffold_generator.rb +4 -4
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/guard.rb +7 -7
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/i18n.rb +1 -1
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/layout.html.erb +20 -0
- data/ixtlan_rails_templates.rb +537 -0
- data/lib/ixtlan/audit_config.rb +5 -5
- data/lib/ixtlan/child_path.rb +2 -2
- data/lib/ixtlan/cms_script.rb +4 -4
- data/lib/ixtlan/controllers/authentications_controller.rb +7 -2
- data/lib/ixtlan/controllers/configurations_controller.rb +15 -6
- data/lib/ixtlan/controllers/domains_controller.rb +99 -0
- data/lib/ixtlan/controllers/groups_controller.rb +105 -0
- data/lib/ixtlan/controllers/locales_controller.rb +99 -0
- data/lib/ixtlan/controllers/permissions_controller.rb +5 -0
- data/lib/ixtlan/controllers/phrases_controller.rb +26 -22
- data/lib/ixtlan/controllers/search_query.rb +24 -0
- data/lib/ixtlan/controllers/texts_controller.rb +5 -5
- data/lib/ixtlan/controllers/users_controller.rb +117 -0
- data/lib/ixtlan/controllers/word_bundles_controller.rb +13 -8
- data/lib/ixtlan/digest.rb +3 -3
- data/lib/ixtlan/guard.rb +11 -12
- data/lib/ixtlan/logger_config.rb +11 -11
- data/lib/ixtlan/mailer/error_notification.erb +1 -0
- data/lib/ixtlan/mailer/password.erb +1 -0
- data/lib/ixtlan/mailer.rb +27 -0
- data/lib/ixtlan/models/authentication.rb +9 -6
- data/lib/ixtlan/models/configuration.rb +21 -37
- data/lib/ixtlan/models/configuration_locale.rb +3 -3
- data/lib/ixtlan/models/domain.rb +44 -0
- data/lib/ixtlan/models/domain_group_user.rb +22 -0
- data/lib/ixtlan/models/group.rb +82 -16
- data/lib/ixtlan/models/group_locale_user.rb +4 -4
- data/lib/ixtlan/models/group_user.rb +7 -7
- data/lib/ixtlan/models/i18n_text.rb +26 -26
- data/lib/ixtlan/models/locale.rb +17 -5
- data/lib/ixtlan/models/permission.rb +3 -2
- data/lib/ixtlan/models/phrase.rb +15 -15
- data/lib/ixtlan/models/role.rb +5 -5
- data/lib/ixtlan/models/translation.rb +9 -9
- data/lib/ixtlan/models/update_children.rb +74 -0
- data/lib/ixtlan/models/user.rb +108 -16
- data/lib/ixtlan/models/word.rb +2 -1
- data/lib/ixtlan/models.rb +1 -0
- data/lib/ixtlan/modified_by.rb +9 -7
- data/lib/ixtlan/monkey_patches.rb +5 -5
- data/lib/ixtlan/optimistic_persistence.rb +2 -2
- data/lib/ixtlan/optimistic_persistence_module.rb +3 -3
- data/lib/ixtlan/optimistic_persistence_validation.rb +2 -2
- data/lib/ixtlan/passwords.rb +15 -13
- data/lib/ixtlan/rails/error_handling.rb +41 -40
- data/lib/ixtlan/rails/guard.rb +0 -1
- data/lib/ixtlan/rails/migrations.rb +75 -0
- data/lib/ixtlan/rails/session_timeout.rb +16 -14
- data/lib/ixtlan/rails/timestamps_modified_by_filter.rb +1 -1
- data/lib/ixtlan/rails/unrestful_authentication.rb +4 -4
- data/lib/ixtlan/rolling_file.rb +3 -3
- data/lib/ixtlan/session.rb +4 -3
- data/lib/ixtlan/user_logger.rb +13 -9
- data/lib/ixtlan/version.rb +1 -1
- data/spec/authentication_spec.rb +1 -1
- data/spec/guard_spec.rb +4 -4
- data/spec/guards/samples.rb +7 -7
- data/spec/modified_by_spec.rb +82 -0
- data/spec/optimistic_persistence_spec.rb +58 -0
- data/spec/phrase_spec.rb +119 -0
- data/spec/session_timeout_spec.rb +59 -0
- data/spec/spec_helper.rb +6 -5
- data/spec/text_collection_spec.rb +88 -0
- data/spec/text_spec.rb +105 -0
- data/spec/unrestful_authentication_spec.rb +142 -0
- data/spec/user_logger_spec.rb +105 -0
- data/spec/user_spec.rb +249 -0
- data/whitespace.rb +31 -0
- metadata +76 -50
- data/lib/ixtlan/error_notifier/error_notification.rhtml +0 -1
|
@@ -9,7 +9,7 @@ module Ixtlan
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def internal_server_error(exception)
|
|
12
|
-
dump_error(exception
|
|
12
|
+
dump_error(exception)
|
|
13
13
|
status = :internal_server_error
|
|
14
14
|
error_page(:internal_server_error, exception) do |exception|
|
|
15
15
|
"internal server error: #{exception.class.name}"
|
|
@@ -17,19 +17,27 @@ module Ixtlan
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def error_page(status, exception, &block)
|
|
20
|
-
respond_to do |format|
|
|
21
|
-
format.html {
|
|
20
|
+
respond_to do |format|
|
|
21
|
+
format.html {
|
|
22
22
|
@notice = block.call(exception)
|
|
23
23
|
if logged_in?
|
|
24
|
-
|
|
24
|
+
render_error_page_with_session(status)
|
|
25
25
|
else
|
|
26
|
-
|
|
26
|
+
render_error_page(status)
|
|
27
27
|
end
|
|
28
28
|
}
|
|
29
29
|
format.xml { head status }
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def render_error_page_with_session(status)
|
|
34
|
+
render :template => "errors/error", :status => status
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def render_error_page(status)
|
|
38
|
+
render :template => "sessions/login", :status => status
|
|
39
|
+
end
|
|
40
|
+
|
|
33
41
|
def page_not_found(exception)
|
|
34
42
|
log_user_error(exception)
|
|
35
43
|
status = rescue_responses[exception.class.name]
|
|
@@ -39,44 +47,37 @@ module Ixtlan
|
|
|
39
47
|
|
|
40
48
|
def stale_resource(exception)
|
|
41
49
|
log_user_error(exception)
|
|
42
|
-
respond_to do |format|
|
|
43
|
-
format.html {
|
|
50
|
+
respond_to do |format|
|
|
51
|
+
format.html {
|
|
44
52
|
render :template => "errors/stale", :status => :conflict
|
|
45
53
|
}
|
|
46
54
|
format.xml { head :conflict }
|
|
47
55
|
end
|
|
48
56
|
end
|
|
49
57
|
|
|
50
|
-
def dump_error(exception
|
|
58
|
+
def dump_error(exception)
|
|
51
59
|
log_user_error(exception)
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
begin
|
|
61
|
+
config = Object.full_const_get(::Ixtlan::Models::CONFIGURATION).instance
|
|
62
|
+
dumper = DumpError.new(config.notification_sender_email,
|
|
63
|
+
config.notification_recipient_emails,
|
|
64
|
+
config.errors_dump_directory)
|
|
65
|
+
rescue => e
|
|
66
|
+
# maybe that gives a double entry in the log file
|
|
67
|
+
log_user_error(e)
|
|
68
|
+
# at least dump the file !!
|
|
69
|
+
dumper = DumpError.new
|
|
70
|
+
end
|
|
54
71
|
dumper.dump(self, exception)
|
|
55
72
|
end
|
|
56
73
|
end
|
|
57
|
-
|
|
58
|
-
class ErrorNotifier < ActionMailer::Base
|
|
59
|
-
require 'pathname'
|
|
60
|
-
|
|
61
|
-
def error_notification(email_from, email_to, exception, error_file)
|
|
62
|
-
@subject = exception.to_s
|
|
63
|
-
@body = {:text => "#{error_file}"}
|
|
64
|
-
@recipients = email_to
|
|
65
|
-
@from = email_from
|
|
66
|
-
@sent_on = Time.now
|
|
67
|
-
@headers = {}
|
|
68
|
-
path = Pathname(__FILE__).parent.dirname.to_s
|
|
69
|
-
view_paths << path unless view_paths.member? path
|
|
70
|
-
@template = "error_notification.rhtml"
|
|
71
|
-
end
|
|
72
74
|
|
|
73
|
-
end
|
|
74
|
-
|
|
75
75
|
class DumpError
|
|
76
|
-
|
|
77
|
-
def initialize(email_from, email_to, errors_dir =
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
|
|
77
|
+
def initialize(email_from = nil, email_to = nil, errors_dir = nil)
|
|
78
|
+
errors_dir ||= "#{RAILS_ROOT}/log/errors"
|
|
79
|
+
FileUtils.mkdir_p(errors_dir || "#{RAILS_ROOT}/log/errors")
|
|
80
|
+
@errors_dir = Dir.new(errors_dir)
|
|
80
81
|
@email_from = email_from
|
|
81
82
|
@email_to = email_to
|
|
82
83
|
end
|
|
@@ -84,35 +85,35 @@ module Ixtlan
|
|
|
84
85
|
def dump(controller, exception)
|
|
85
86
|
time = Time.now
|
|
86
87
|
error_log_id = "#{time.tv_sec}#{time.tv_usec}"
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
log_file = File.join(@errors_dir.path.to_s, "error-#{error_log_id}.log")
|
|
89
90
|
logger = Logger.new(log_file)
|
|
90
|
-
|
|
91
|
+
|
|
91
92
|
dump_environment(logger, exception, controller)
|
|
92
|
-
|
|
93
|
+
Ixtlan::Mailer.deliver_error_notification(@email_from, @email_to, exception, log_file) unless (@email_to.blank? || @email_from.blank?)
|
|
93
94
|
log_file
|
|
94
|
-
end
|
|
95
|
+
end
|
|
95
96
|
|
|
96
97
|
private
|
|
97
98
|
|
|
98
99
|
def dump_environment_header(logger, header)
|
|
99
100
|
logger.error("\n===================================================================\n#{header}\n===================================================================\n");
|
|
100
101
|
end
|
|
101
|
-
|
|
102
|
+
|
|
102
103
|
def dump_environment(logger, exception, controller)
|
|
103
104
|
dump_environment_header(logger, "REQUEST DUMP");
|
|
104
105
|
dump_hashmap(logger, controller.request.env)
|
|
105
|
-
|
|
106
|
+
|
|
106
107
|
dump_environment_header(logger, "RESPONSE DUMP");
|
|
107
108
|
dump_hashmap(logger, controller.response.headers)
|
|
108
|
-
|
|
109
|
+
|
|
109
110
|
dump_environment_header(logger, "SESSION DUMP");
|
|
110
111
|
dump_hashmap(logger, controller.session)
|
|
111
|
-
|
|
112
|
+
|
|
112
113
|
dump_environment_header(logger, "PARAMETER DUMP");
|
|
113
114
|
map = {}
|
|
114
115
|
dump_hashmap(logger, controller.params.each{ |k,v| map[k]=v })
|
|
115
|
-
|
|
116
|
+
|
|
116
117
|
dump_environment_header(logger, "EXCEPTION");
|
|
117
118
|
logger.error("#{exception.class}:#{exception.message}")
|
|
118
119
|
logger.error("\t" + exception.backtrace.join("\n\t"))
|
data/lib/ixtlan/rails/guard.rb
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Ixtlan
|
|
2
|
+
module Rails
|
|
3
|
+
class Migrations
|
|
4
|
+
|
|
5
|
+
# assume no namespaces !!!
|
|
6
|
+
USER = Object.const_get(Ixtlan::Models::USER)
|
|
7
|
+
GROUP = Object.const_get(Ixtlan::Models::GROUP)
|
|
8
|
+
LOCALE = Object.const_get(Ixtlan::Models::LOCALE)
|
|
9
|
+
DOMAIN = Object.const_get(Ixtlan::Models::DOMAIN)
|
|
10
|
+
CONFIGURATION = Object.const_get(Ixtlan::Models::CONFIGURATION)
|
|
11
|
+
|
|
12
|
+
def self.create_user
|
|
13
|
+
USER.auto_migrate!
|
|
14
|
+
LOCALE.auto_migrate!
|
|
15
|
+
GROUP.auto_migrate!
|
|
16
|
+
Ixtlan::Models::GroupUser.auto_migrate!
|
|
17
|
+
Ixtlan::Models::GroupLocaleUser.auto_migrate!
|
|
18
|
+
Ixtlan::Models::DomainGroupUser.auto_migrate!
|
|
19
|
+
|
|
20
|
+
u = USER.new(:login => 'root', :email => 'root@example.com', :name => 'Superuser', :id => 1)
|
|
21
|
+
u.created_at = DateTime.now
|
|
22
|
+
u.updated_at = u.created_at
|
|
23
|
+
u.created_by_id = 1
|
|
24
|
+
u.updated_by_id = 1
|
|
25
|
+
u.reset_password
|
|
26
|
+
u.save!
|
|
27
|
+
|
|
28
|
+
g = GROUP.create(:name => 'root', :current_user => u)
|
|
29
|
+
u.groups << g
|
|
30
|
+
u.save
|
|
31
|
+
|
|
32
|
+
a = USER.create(:login => 'admin', :email => 'admin@example.com', :name => 'Administrator', :id => 2, :current_user => u)
|
|
33
|
+
a.reset_password
|
|
34
|
+
a.save!
|
|
35
|
+
a.groups << GROUP.create(:name => 'admin', :current_user => u)
|
|
36
|
+
a.save
|
|
37
|
+
|
|
38
|
+
users = GROUP.create(:name => 'users', :current_user => u)
|
|
39
|
+
locales = GROUP.create(:name => 'locales', :current_user => u)
|
|
40
|
+
domains = GROUP.create(:name => 'domains', :current_user => u)
|
|
41
|
+
|
|
42
|
+
File.open("root", 'w') { |f| f.puts "root\n#{u.password}\n\nadmin\n#{a.password}\n\n" }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.create_configuration
|
|
46
|
+
Ixtlan::Models::ConfigurationLocale.auto_migrate!
|
|
47
|
+
CONFIGURATION.auto_migrate!
|
|
48
|
+
CONFIGURATION.create(:session_idle_timeout => 10, :keep_audit_logs => 3, :current_user => USER.first)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.create_locale
|
|
52
|
+
LOCALE.auto_migrate!
|
|
53
|
+
# get/create default locale
|
|
54
|
+
LOCALE.create(:code => LOCALE::DEFAULT, :current_user => USER.first)
|
|
55
|
+
# get/create "every" locale
|
|
56
|
+
LOCALE.create(:code => LOCALE::ALL, :current_user => USER.first)
|
|
57
|
+
|
|
58
|
+
# root user has access to ALL locales
|
|
59
|
+
Ixtlan::Models::GroupLocaleUser.create(:group => Group.first, :user => USER.first, :locale => LOCALE.every)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.create_domain
|
|
63
|
+
DOMAIN.auto_migrate!
|
|
64
|
+
# get/create "every" domain
|
|
65
|
+
DOMAIN.create(:name => DOMAIN::ALL, :current_user => User.first)
|
|
66
|
+
|
|
67
|
+
Ixtlan::Models::DomainGroupUser.create(:group => Group.first, :user => USER.first, :domain => DOMAIN.every)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.create_text
|
|
71
|
+
I18nText.auto_migrate!
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -5,7 +5,7 @@ module Ixtlan
|
|
|
5
5
|
def self.included(base)
|
|
6
6
|
base.send(:include, InstanceMethods)
|
|
7
7
|
end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
module InstanceMethods
|
|
10
10
|
private
|
|
11
11
|
|
|
@@ -14,16 +14,16 @@ module Ixtlan
|
|
|
14
14
|
def session_user_logger
|
|
15
15
|
@session_user_logger ||= UserLogger.new(Ixtlan::Rails::SessionTimeout)
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def expire_session
|
|
19
19
|
session.clear
|
|
20
20
|
# reset_session
|
|
21
21
|
render_session_timeout
|
|
22
22
|
return false
|
|
23
23
|
end
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
public
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
def check_session_expiry
|
|
28
28
|
if !session[:expires_at].nil? and session[:expires_at] < DateTime.now
|
|
29
29
|
# Session has expired.
|
|
@@ -35,23 +35,25 @@ module Ixtlan
|
|
|
35
35
|
return true
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
# IP binding is not very useful in the wild since some ISP use
|
|
40
|
+
# a different IP for each request, i.e. the session uses many IPs
|
|
39
41
|
def check_session_ip_binding
|
|
40
42
|
if !session[:session_ip].nil? and session[:session_ip] != request.headers['REMOTE_ADDR']
|
|
41
43
|
# client IP has changed
|
|
42
44
|
session_user_logger.log(self, "IP changed from #{session[:session_ip]} to #{request.headers['REMOTE_ADDR']}")
|
|
43
45
|
expire_session
|
|
44
46
|
else
|
|
45
|
-
# Assign client IP
|
|
47
|
+
# Assign client IP
|
|
46
48
|
session[:session_ip] = request.headers['REMOTE_ADDR']
|
|
47
49
|
return true
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
|
-
|
|
52
|
+
|
|
51
53
|
def check_session
|
|
52
|
-
|
|
54
|
+
check_session_browser_signature && check_session_expiry
|
|
53
55
|
end
|
|
54
|
-
|
|
56
|
+
|
|
55
57
|
def check_session_browser_signature
|
|
56
58
|
if !session[:session_browser_signature].nil? and session[:session_browser_signature] != retrieve_browser_signature
|
|
57
59
|
# browser signature has changed
|
|
@@ -64,24 +66,24 @@ module Ixtlan
|
|
|
64
66
|
return true
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
|
-
|
|
69
|
+
|
|
68
70
|
def retrieve_browser_signature
|
|
69
71
|
[request.headers['HTTP_USER_AGENT'],
|
|
70
72
|
request.headers['HTTP_ACCEPT_LANGUAGE'],
|
|
71
73
|
request.headers['HTTP_ACCEPT_ENCODING'],
|
|
72
74
|
request.headers['HTTP_ACCEPT']].join('|')
|
|
73
75
|
end
|
|
74
|
-
|
|
76
|
+
|
|
75
77
|
def render_session_timeout
|
|
76
|
-
respond_to do |format|
|
|
77
|
-
format.html {
|
|
78
|
+
respond_to do |format|
|
|
79
|
+
format.html {
|
|
78
80
|
@notice = "session timeout" unless @notice
|
|
79
81
|
render :template => "sessions/login"
|
|
80
82
|
}
|
|
81
83
|
format.xml { head :unauthorized }
|
|
82
84
|
end
|
|
83
85
|
end
|
|
84
|
-
|
|
86
|
+
|
|
85
87
|
def session_timeout
|
|
86
88
|
CONFIG.instance.session_idle_timeout
|
|
87
89
|
end
|
|
@@ -21,7 +21,7 @@ module Rails
|
|
|
21
21
|
def current_user
|
|
22
22
|
session[:user] = login_from_session if logged_in? and session[:user].nil?
|
|
23
23
|
session[:user]
|
|
24
|
-
end
|
|
24
|
+
end
|
|
25
25
|
|
|
26
26
|
def current_user=(new_user)
|
|
27
27
|
session[:user_id] = new_user ? new_user.id : nil
|
|
@@ -45,13 +45,13 @@ module Rails
|
|
|
45
45
|
end
|
|
46
46
|
else
|
|
47
47
|
case(request.method)
|
|
48
|
-
when :get
|
|
48
|
+
when :get
|
|
49
49
|
session.clear
|
|
50
50
|
render_login_page
|
|
51
51
|
when :post
|
|
52
52
|
user = login_from_params
|
|
53
53
|
if user.instance_of? String
|
|
54
|
-
authentication_logger.log_user(params[:login], user + " from IP #{request.headers['REMOTE_ADDR']}")
|
|
54
|
+
authentication_logger.log_user(params[:login] || params[:authentication][:login], user + " from IP #{request.headers['REMOTE_ADDR']}")
|
|
55
55
|
session.clear
|
|
56
56
|
render_access_denied
|
|
57
57
|
else
|
|
@@ -128,7 +128,7 @@ module Rails
|
|
|
128
128
|
end
|
|
129
129
|
format.xml { head :ok }
|
|
130
130
|
end
|
|
131
|
-
end
|
|
131
|
+
end
|
|
132
132
|
end
|
|
133
133
|
end
|
|
134
134
|
end
|
data/lib/ixtlan/rolling_file.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require 'logging'
|
|
2
2
|
|
|
3
3
|
module Ixtlan
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
class RollingFile < ::Logging::Appenders::RollingFile
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
def current_logfile
|
|
8
8
|
"#{@filename_base}_#{Time.now.strftime(@date_pattern)}.#{@extension}"
|
|
9
9
|
end
|
|
@@ -31,7 +31,7 @@ module Ixtlan
|
|
|
31
31
|
::File.delete file
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
|
-
end
|
|
34
|
+
end
|
|
35
35
|
|
|
36
36
|
def write( event )
|
|
37
37
|
str = event.instance_of?(::Logging::LogEvent) ?
|
data/lib/ixtlan/session.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'rack_datamapper/session/abstract/store'
|
|
2
2
|
module Ixtlan
|
|
3
3
|
class Session < DataMapper::Session::Abstract::Session
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
def data=(data)
|
|
6
6
|
d = {}
|
|
7
7
|
data.each{|k,v| d[k.to_sym] = v}
|
|
@@ -10,9 +10,10 @@ module Ixtlan
|
|
|
10
10
|
@expires_at = d.delete(:expires_at)
|
|
11
11
|
attribute_set(:data, ::Base64.encode64(Marshal.dump(d)))
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def data
|
|
15
|
-
|
|
15
|
+
# user string for flash entry to allow the rails falsh to work properly !
|
|
16
|
+
Marshal.load(::Base64.decode64(attribute_get(:data))).merge({:user => @user, "flash" => @flash, :expires_at => @expires_at})
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
end
|
data/lib/ixtlan/user_logger.rb
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
require 'slf4r'
|
|
2
2
|
module Ixtlan
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
class UserLogger
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
def initialize(arg)
|
|
7
7
|
@logger = Slf4r::LoggerFacade.new(arg)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
private
|
|
10
|
+
private
|
|
11
11
|
|
|
12
12
|
def login_from(controller)
|
|
13
13
|
user = controller.respond_to?(:current_user) ? controller.send(:current_user) : nil
|
|
14
14
|
user.nil? ? nil: user.login
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
public
|
|
17
|
+
public
|
|
18
18
|
|
|
19
19
|
def log(controller, message = nil, &block)
|
|
20
20
|
log_user(login_from(controller), message, &block)
|
|
@@ -30,20 +30,24 @@ module Ixtlan
|
|
|
30
30
|
else
|
|
31
31
|
audit = controller.instance_variable_get("@#{controller.params[:controller].singular.to_sym}")
|
|
32
32
|
if(audit)
|
|
33
|
-
|
|
33
|
+
errors = if(audit.respond_to?(:errors) && !audit.errors.empty?)
|
|
34
|
+
" - errors: " + audit.errors.full_messages.join(", ")
|
|
35
|
+
end
|
|
36
|
+
audit_log = audit.respond_to?(:to_log) ? audit.to_log : "#{audit.model}(#{audit.key})"
|
|
37
|
+
"#{controller.params[:controller]}##{controller.params[:action]} #{audit_log}#{as_xml}#{message}#{errors}"
|
|
34
38
|
else
|
|
35
39
|
"#{controller.params[:controller]}##{controller.params[:action]}#{as_xml}#{message}"
|
|
36
40
|
end
|
|
37
41
|
end
|
|
38
42
|
else
|
|
39
|
-
"params=#{controller.params.inspect}"
|
|
43
|
+
"params=#{controller.params.inspect}#{message}"
|
|
40
44
|
end
|
|
41
45
|
end
|
|
42
46
|
end
|
|
43
|
-
|
|
47
|
+
|
|
44
48
|
def log_user(user, message = nil, &block)
|
|
45
|
-
user
|
|
46
|
-
@logger.info {"[#{user}] #{message}#{block.call if block}" }
|
|
49
|
+
user ||= "???"
|
|
50
|
+
@logger.info {"[#{user}] #{message}#{block.call if block}" }
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
end
|
data/lib/ixtlan/version.rb
CHANGED
data/spec/authentication_spec.rb
CHANGED
|
@@ -24,7 +24,7 @@ describe Ixtlan::Models::Authentication do
|
|
|
24
24
|
|
|
25
25
|
it "should" do
|
|
26
26
|
xml = @authentication.to_xml
|
|
27
|
-
xml.gsub!(/[0-9-]{10}T[0-9+-:]{14}/, "").gsub!(/ type='[^']+'/, '').gsub!(/<created_at><\/created_at>/, "<created_at/>").should == "<authentication><login>marvin2</login><user><id>1356</id><login>marvin2</login><name>marvin the robot</name><email>marvin@universe.example.com</email><language>xx</language><created_at/><updated_at></updated_at><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><groups><group><id>1356</id><name>marvin2_root</name><created_at/><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><locales><locale><code>DEFAULT</code><created_at/></locale><locale><code>en</code><created_at/></locale></locales></group></groups></user></authentication>"
|
|
27
|
+
xml.gsub!(/[0-9-]{10}T[0-9+-:]{14}/, "").gsub!(/ type='[^']+'/, '').gsub!(/<created_at><\/created_at>/, "<created_at/>").gsub!(/<locale><id>[0-9]*<\/id>/, "<locale>").should == "<authentication><id>1</id><login>marvin2</login><user><id>1356</id><login>marvin2</login><name>marvin the robot</name><email>marvin@universe.example.com</email><language>xx</language><created_at/><updated_at></updated_at><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><groups><group><id>1356</id><name>marvin2_root</name><created_at/><created_by_id>1356</created_by_id><updated_by_id>1356</updated_by_id><locales><locale><code>DEFAULT</code><created_at/></locale><locale><code>en</code><created_at/></locale></locales></group></groups></user></authentication>"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
end
|
data/spec/guard_spec.rb
CHANGED
|
@@ -7,13 +7,13 @@ module ActionView
|
|
|
7
7
|
end
|
|
8
8
|
module Erector
|
|
9
9
|
class Widget
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
attr_writer :controller
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
class Helpers
|
|
14
14
|
attr_accessor :controller
|
|
15
15
|
end
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
def helpers
|
|
18
18
|
@helpers ||= Helpers.new
|
|
19
19
|
@helpers.controller = @controller
|
|
@@ -38,7 +38,7 @@ describe Ixtlan::Models::Permission do
|
|
|
38
38
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
class Controller
|
|
41
|
+
class Controller
|
|
42
42
|
include ActionController::Base
|
|
43
43
|
end
|
|
44
44
|
|
data/spec/guards/samples.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Ixtlan::Guard.initialize(:permissions,
|
|
2
|
-
{ :show => [:guest],
|
|
3
|
-
:edit => [:root, :admin],
|
|
4
|
-
:update => [:root, :admin],
|
|
1
|
+
Ixtlan::Guard.initialize(:permissions,
|
|
2
|
+
{ :show => [:guest],
|
|
3
|
+
:edit => [:root, :admin],
|
|
4
|
+
:update => [:root, :admin],
|
|
5
5
|
:destroy => [:root] }
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
-
Ixtlan::Guard.initialize(:configurations,
|
|
9
|
-
{ :show => [:admin],
|
|
10
|
-
:edit => [:root],
|
|
8
|
+
Ixtlan::Guard.initialize(:configurations,
|
|
9
|
+
{ :show => [:admin],
|
|
10
|
+
:edit => [:root],
|
|
11
11
|
:update => [:root] }
|
|
12
12
|
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require Pathname(__FILE__).dirname + 'spec_helper.rb'
|
|
3
|
+
require 'ixtlan' / 'modified_by'
|
|
4
|
+
|
|
5
|
+
class User
|
|
6
|
+
include DataMapper::Resource
|
|
7
|
+
|
|
8
|
+
property :login, String, :key => true
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class AuditedName
|
|
12
|
+
include DataMapper::Resource
|
|
13
|
+
|
|
14
|
+
# TODO remove serial
|
|
15
|
+
property :id, Serial
|
|
16
|
+
property :name, String, :length => 2..255#, :key => true
|
|
17
|
+
|
|
18
|
+
modified_by User
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe Ixtlan::ModifiedBy do
|
|
22
|
+
|
|
23
|
+
before :each do
|
|
24
|
+
@user = User.create(:login => 'spock')
|
|
25
|
+
@second = User.create(:login => 'dr pill')
|
|
26
|
+
@name = AuditedName.create(:name => 'kirk', :current_user => @user)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should create resource with modified by properties' do
|
|
30
|
+
@name.new?.should be_false
|
|
31
|
+
@name.instance_variable_get(:@current_user).should be_nil
|
|
32
|
+
@name.created_by.should == @user
|
|
33
|
+
@name.updated_by.should == @user
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should new/save resource with modified by properties' do
|
|
37
|
+
name = AuditedName.new(:name => 'seven of nine')
|
|
38
|
+
name.current_user = @user
|
|
39
|
+
name.save.should be_true
|
|
40
|
+
name.created_by.should == @user
|
|
41
|
+
name.updated_by.should == @user
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should modify updated_by on change' do
|
|
45
|
+
@name.current_user = @second
|
|
46
|
+
@name.name = "scotty"
|
|
47
|
+
@name.save.should be_true
|
|
48
|
+
@name.created_by.should == @user
|
|
49
|
+
@name.updated_by.should == @second
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'should modify updated_by on update' do
|
|
53
|
+
@name.update(:name => "captain kirk", :current_user => @second)
|
|
54
|
+
@name.instance_variable_get(:@current_user).should be_nil
|
|
55
|
+
@name.updated_by.should == @second
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'should not modify updated_by on update without change' do
|
|
59
|
+
@name.update(:name => "kirk", :current_user => @second)
|
|
60
|
+
@name.updated_by.should == @user
|
|
61
|
+
@name.instance_variable_get(:@current_user).should be_nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'should modify updated_by on update with current_user' do
|
|
65
|
+
@name.current_user = @second
|
|
66
|
+
@name.update(:name => "captain kirk")
|
|
67
|
+
@name.updated_by.should == @second
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'should fail create without current_user' do
|
|
71
|
+
lambda { AuditedName.create(:name => "ohura") }.should raise_error(DataMapper::MissingCurrentUserError)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'should fail save without current_user' do
|
|
75
|
+
@name.name = "ohura"
|
|
76
|
+
lambda { @name.save }.should raise_error(DataMapper::MissingCurrentUserError)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'should fail update without current_user' do
|
|
80
|
+
lambda { @name.update(:name => "ohura") }.should raise_error(DataMapper::MissingCurrentUserError)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require Pathname(__FILE__).dirname + 'spec_helper.rb'
|
|
3
|
+
|
|
4
|
+
require 'ixtlan/optimistic_persistence'
|
|
5
|
+
|
|
6
|
+
class Name
|
|
7
|
+
include DataMapper::Resource
|
|
8
|
+
|
|
9
|
+
property :id, Serial
|
|
10
|
+
property :name, String, :length => 2..255
|
|
11
|
+
|
|
12
|
+
timestamps :updated_at
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Number
|
|
16
|
+
include DataMapper::Resource
|
|
17
|
+
|
|
18
|
+
property :id, Serial
|
|
19
|
+
property :number, Integer
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "Ixtlan::OptimisticPersistence" do
|
|
23
|
+
|
|
24
|
+
before :each do
|
|
25
|
+
@name = Name.create(:name => "frodo")
|
|
26
|
+
@second = Name.get(@name.id)
|
|
27
|
+
@number = Number.first_or_create(:number => 123)
|
|
28
|
+
@other = Number.first
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should save' do
|
|
32
|
+
@name.name = "gandalf"
|
|
33
|
+
@name.save.should be_true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should fail' do
|
|
37
|
+
@name.name = "gandalf"
|
|
38
|
+
sleep 1
|
|
39
|
+
@name.save.should be_true
|
|
40
|
+
@second.name = "saroman"
|
|
41
|
+
lambda { @second.save }.should raise_error(DataMapper::StaleResourceError)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should fail on key change' do
|
|
45
|
+
pending "maybe bug in datamapper"
|
|
46
|
+
@name.id = 11
|
|
47
|
+
@name.save.should be_true
|
|
48
|
+
@second.id = 111
|
|
49
|
+
lambda { @second.save }.should raise_error(DataMapper::StaleResourceError)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'should treat non optimistic resources as usual' do
|
|
53
|
+
@number.number = 345
|
|
54
|
+
@number.save.should be_true
|
|
55
|
+
@other.number = 987
|
|
56
|
+
@other.save.should be_true
|
|
57
|
+
end
|
|
58
|
+
end
|