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