ixtlan 0.2.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/Manifest.txt +64 -0
- data/README.txt +86 -0
- data/Rakefile +38 -0
- data/generators/ixtlan_datamapper_model/ixtlan_datamapper_model_generator.rb +20 -0
- data/generators/ixtlan_datamapper_model/templates/model.rb +17 -0
- data/generators/ixtlan_datamapper_rspec_model/ixtlan_datamapper_rspec_model_generator.rb +20 -0
- data/generators/ixtlan_datamapper_rspec_model/templates/model_spec.rb +58 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/ixtlan_datamapper_rspec_scaffold_generator.rb +37 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/controller.rb +99 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/controller_spec.rb +206 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/guard.rb +8 -0
- data/generators/ixtlan_datamapper_rspec_scaffold/templates/i18n.rb +11 -0
- data/lib/dm-serializer/common.rb +28 -0
- data/lib/dm-serializer/to_xml.rb +99 -0
- data/lib/dm-serializer/xml_serializers/libxml.rb +42 -0
- data/lib/dm-serializer/xml_serializers/nokogiri.rb +36 -0
- data/lib/dm-serializer/xml_serializers/rexml.rb +30 -0
- data/lib/dm-serializer/xml_serializers.rb +17 -0
- data/lib/dm-serializer.rb +1 -0
- data/lib/ixtlan/audit_config.rb +35 -0
- data/lib/ixtlan/cms_script.rb +29 -0
- data/lib/ixtlan/controllers/texts_controller.rb +65 -0
- data/lib/ixtlan/digest.rb +15 -0
- data/lib/ixtlan/error_notifier/error_notification.rhtml +1 -0
- data/lib/ixtlan/guard.rb +125 -0
- data/lib/ixtlan/logger_config.rb +65 -0
- data/lib/ixtlan/models/authentication.rb +30 -0
- data/lib/ixtlan/models/configuration.rb +74 -0
- data/lib/ixtlan/models/configuration_locale.rb +19 -0
- data/lib/ixtlan/models/group.rb +69 -0
- data/lib/ixtlan/models/group_locale_user.rb +22 -0
- data/lib/ixtlan/models/group_user.rb +39 -0
- data/lib/ixtlan/models/locale.rb +37 -0
- data/lib/ixtlan/models/permission.rb +29 -0
- data/lib/ixtlan/models/phrase.rb +71 -0
- data/lib/ixtlan/models/role.rb +35 -0
- data/lib/ixtlan/models/text.rb +140 -0
- data/lib/ixtlan/models/translation.rb +40 -0
- data/lib/ixtlan/models/user.rb +112 -0
- data/lib/ixtlan/models/word.rb +12 -0
- data/lib/ixtlan/models.rb +13 -0
- data/lib/ixtlan/modified_by.rb +80 -0
- data/lib/ixtlan/monkey_patches.rb +38 -0
- data/lib/ixtlan/optimistic_persistence.rb +20 -0
- data/lib/ixtlan/optimistic_persistence_module.rb +22 -0
- data/lib/ixtlan/optimistic_persistence_validation.rb +21 -0
- data/lib/ixtlan/passwords.rb +19 -0
- data/lib/ixtlan/rails/audit.rb +22 -0
- data/lib/ixtlan/rails/error_handling.rb +130 -0
- data/lib/ixtlan/rails/guard.rb +11 -0
- data/lib/ixtlan/rails/session_timeout.rb +93 -0
- data/lib/ixtlan/rails/timestamps_modified_by_filter.rb +33 -0
- data/lib/ixtlan/rails/unrestful_authentication.rb +137 -0
- data/lib/ixtlan/rolling_file.rb +61 -0
- data/lib/ixtlan/session.rb +18 -0
- data/lib/ixtlan/user_logger.rb +49 -0
- data/lib/ixtlan/version.rb +3 -0
- data/lib/ixtlan.rb +2 -0
- data/lib/models.rb +14 -0
- data/spec/authentication_spec.rb +30 -0
- data/spec/guard_spec.rb +125 -0
- data/spec/guards/samples.rb +12 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +170 -0
- metadata +210 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
class Word < Text
|
4
|
+
|
5
|
+
alias :to_x :to_xml_document
|
6
|
+
def to_xml_document(opts, doc = nil)
|
7
|
+
opts.merge!({:element_name => "word", :exclude => [:id, :locale_code, :current, :previous, :updated_at, :approved_at, :updated_by_id, :approved_by_id, :version]})
|
8
|
+
to_x(opts, doc)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Models
|
3
|
+
AUTHENTICATION = "::Ixtlan::Models::Authentication" unless const_defined? "AUTHENTICATION"
|
4
|
+
USER = "::Ixtlan::Models::User" unless const_defined? "USER"
|
5
|
+
GROUP = "::Ixtlan::Models::Group" unless const_defined? "GROUP"
|
6
|
+
ROLE = "::Ixtlan::Models::Role" unless const_defined? "ROLE"
|
7
|
+
PERMISSION = "::Ixtlan::Models::Permission" unless const_defined? "PERMISSION"
|
8
|
+
LOCALE = "::Ixtlan::Models::Locale" unless const_defined? "LOCALE"
|
9
|
+
CONFIGURATION = "::Ixtlan::Models::Configuration" unless const_defined? "CONFIGURATION"
|
10
|
+
TRANSLATION = "::Ixtlan::Models::Translation" unless const_defined? "TRANSLATION"
|
11
|
+
TEXT = "::Ixtlan::Models::Text" unless const_defined? "TEXT"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#require 'dm-core'
|
2
|
+
module Ixtlan
|
3
|
+
|
4
|
+
module ModifiedBy
|
5
|
+
extend ::DataMapper::Chainable
|
6
|
+
|
7
|
+
MODIFIED_BY_PROPERTIES = {
|
8
|
+
:updated_by => lambda {|r, u| r.updated_by = u},
|
9
|
+
:created_by => lambda {|r, u| r.created_by = u if r.new? }
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def self.included(model)
|
13
|
+
model.after :save do @current_user = nil; end
|
14
|
+
model.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_user=(user)
|
18
|
+
@current_user = user
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def current_user
|
24
|
+
raise ::DataMapper::MissingCurrentUserError.new("current_user not set. it can be set like any other property") unless @current_user
|
25
|
+
|
26
|
+
@current_user
|
27
|
+
end
|
28
|
+
|
29
|
+
chainable do
|
30
|
+
def new(attributes = {}, &block)
|
31
|
+
current_user= attributes.delete(:current_user)
|
32
|
+
super(&block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
chainable do
|
37
|
+
def save(*args)
|
38
|
+
set_modified_by if dirty?
|
39
|
+
super()
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
chainable do
|
44
|
+
def update!(*args)
|
45
|
+
set_modified_by if dirty?
|
46
|
+
super()
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_modified_by
|
51
|
+
MODIFIED_BY_PROPERTIES.each do |name, setter|
|
52
|
+
if respond_to? name
|
53
|
+
setter.call(self, current_user)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module ClassMethods
|
59
|
+
def modified_by(type, *names)
|
60
|
+
if(names.empty?)
|
61
|
+
modified_by(type, :created_by, :updated_by)
|
62
|
+
else
|
63
|
+
names.each do |name|
|
64
|
+
case name
|
65
|
+
when *MODIFIED_BY_PROPERTIES.keys
|
66
|
+
belongs_to name, :model => type.to_s
|
67
|
+
else
|
68
|
+
raise ::DataMapper::InvalidModifiedByName, "Invalid 'modified by' name '#{name}'"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ::DataMapper::InvalidModifiedByName < RuntimeError; end
|
76
|
+
class ::DataMapper::MissingCurrentUserError < RuntimeError; end
|
77
|
+
|
78
|
+
::DataMapper::Model.append_inclusions self
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
if RUBY_PLATFORM =~ /java/
|
2
|
+
require 'zlib'
|
3
|
+
class Zlib::GzipWriter
|
4
|
+
def <<(arg)
|
5
|
+
write(arg)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
# fix with rails development mode and class reloading
|
10
|
+
# not sure where the exact problem is :-(
|
11
|
+
module Extlib
|
12
|
+
module Assertions
|
13
|
+
def assert_kind_of(name, value, *klasses)
|
14
|
+
# be less strict and allow matching class names to OK as well
|
15
|
+
klasses.each { |k| return if value.kind_of?(k) or value.class.name == k.name }
|
16
|
+
raise ArgumentError, "+\#{name}+ should be \#{klasses.map { |k| k.name } * ' or '}, but was \#{value.class.name}", caller(2)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if RUBY_PLATFORM =~ /java/
|
21
|
+
module DataMapper
|
22
|
+
module Validate
|
23
|
+
class NumericValidator
|
24
|
+
|
25
|
+
def validate_with_comparison(value, cmp, expected, error_message_name, errors, negated = false)
|
26
|
+
return if expected.nil?
|
27
|
+
if cmp == :=~
|
28
|
+
return value =~ expected
|
29
|
+
end
|
30
|
+
comparison = value.send(cmp, expected)
|
31
|
+
return if negated ? !comparison : comparison
|
32
|
+
|
33
|
+
errors << ValidationErrors.default_error_message(error_message_name, field_name, expected)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'ixtlan/optimistic_persistence_module'
|
2
|
+
require 'dm-core'
|
3
|
+
module DataMapper
|
4
|
+
|
5
|
+
class StaleResourceError < StandardError; end
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
module Ixtlan
|
10
|
+
module OptimisticPersistence
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.send(:include, ::Ixtlan::OptimisticPersistenceModule)
|
14
|
+
base.before :valid? do
|
15
|
+
raise ::DataMapper::StaleResourceError.new(model.name + "(#{key}) was stale") if stale?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
::DataMapper::Model.append_inclusions self
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module OptimisticPersistenceModule
|
3
|
+
def stale?
|
4
|
+
if(!new? && prop = properties[:updated_at] && dirty?)
|
5
|
+
updated_at = original_attributes[prop] || properties[:updated_at].get!(self)
|
6
|
+
qu = {}
|
7
|
+
c = model.key_conditions(repository, key)
|
8
|
+
c.each {|p,v| qu[p.name] = v}
|
9
|
+
|
10
|
+
s = self.model.first(qu)
|
11
|
+
if s.nil?
|
12
|
+
false
|
13
|
+
else
|
14
|
+
# use to_s to get it to work in both MRI and JRuby
|
15
|
+
s.updated_at.to_s != updated_at.to_s
|
16
|
+
end
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'ixtlan/optimistic_persistence_module'
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
|
5
|
+
class StaleResource < StandardError; end
|
6
|
+
|
7
|
+
module OptimisticPersistenceValidation
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.send(:include, ::Ixtlan::OptimisticPersistenceModule)
|
11
|
+
base.validates_with_block :stale do
|
12
|
+
if(stale?)
|
13
|
+
[false, "stale resource, please reload the resource"]
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
Model.append_inclusions self
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
class Passwords
|
3
|
+
def self.generate(length=64)
|
4
|
+
# A-Z starting with 97 and having 26 characters
|
5
|
+
# a-z starting with 65 and having 26 characters
|
6
|
+
# 0-9 starting with 48 and lies inside range starting with 33 and having 26 characters
|
7
|
+
offset=[97, 65, 33]
|
8
|
+
|
9
|
+
# collect random characters from the either of the above ranges
|
10
|
+
begin
|
11
|
+
pwd = (0..(length - 1)).collect do
|
12
|
+
j = ActiveSupport::SecureRandom.random_number(78)
|
13
|
+
(offset[j / 26] + (j % 26)).chr
|
14
|
+
end.join
|
15
|
+
end while !((pwd =~ /[a-z]/) && (pwd =~ /[A-Z]/) && (pwd =~ /[!-;]/))
|
16
|
+
pwd
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Rails
|
3
|
+
module AuditBase
|
4
|
+
def self.included(base)
|
5
|
+
base.append_after_filter(Audit)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Audit
|
10
|
+
|
11
|
+
def self.logger
|
12
|
+
@logger ||= UserLogger.new(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.filter(controller)
|
16
|
+
logger.log_action(controller)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
::ActionController::Base.send(:include, Ixtlan::Rails::AuditBase)
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Rails
|
3
|
+
module ErrorHandling
|
4
|
+
|
5
|
+
def log_user_error(exception)
|
6
|
+
UserLogger.new(Ixtlan::Rails::ErrorHandling).log_action(self, " - #{exception.class} - #{exception.message}")
|
7
|
+
log_error(exception)
|
8
|
+
end
|
9
|
+
|
10
|
+
def internal_server_error(exception)
|
11
|
+
dump_error(exception, Object.full_const_get(::Ixtlan::Models.CONFIGURATION).instance)
|
12
|
+
status = :internal_server_error
|
13
|
+
error_page(:internal_server_error, exception) do |exception|
|
14
|
+
"internal server error: #{exception.class.name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def error_page(status, exception, &block)
|
19
|
+
respond_to do |format|
|
20
|
+
format.html {
|
21
|
+
@notice = block.call(exception)
|
22
|
+
if logged_in?
|
23
|
+
render :template => "errors/error", :status => status
|
24
|
+
else
|
25
|
+
render :template => "sessions/login", :status => status
|
26
|
+
end
|
27
|
+
}
|
28
|
+
format.xml { head status }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def page_not_found(exception)
|
33
|
+
log_user_error(exception)
|
34
|
+
status = rescue_responses[exception.class.name]
|
35
|
+
status = status == :internal_server_error ? :not_found : status
|
36
|
+
error_page(status, exception) { "page not found" }
|
37
|
+
end
|
38
|
+
|
39
|
+
def stale_resource(exception)
|
40
|
+
log_user_error(exception)
|
41
|
+
respond_to do |format|
|
42
|
+
format.html {
|
43
|
+
render :template => "errors/stale", :status => :conflict
|
44
|
+
}
|
45
|
+
format.xml { head :conflict }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def dump_error(exception, config)
|
50
|
+
log_user_error(exception)
|
51
|
+
dumper = DumpError.new(config.notification_sender_email,
|
52
|
+
config.notification_recipient_email)
|
53
|
+
dumper.dump(self, exception)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ErrorNotifier < ActionMailer::Base
|
58
|
+
require 'pathname'
|
59
|
+
|
60
|
+
def error_notification(email_from, email_to, exception, error_file)
|
61
|
+
@subject = exception.to_s
|
62
|
+
@body = {:text => "#{error_file}"}
|
63
|
+
@recipients = email_to
|
64
|
+
@from = email_from
|
65
|
+
@sent_on = Time.now
|
66
|
+
@headers = {}
|
67
|
+
path = Pathname(__FILE__).parent.dirname.to_s
|
68
|
+
view_paths << path unless view_paths.member? path
|
69
|
+
@template = "error_notification.rhtml"
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class DumpError
|
75
|
+
|
76
|
+
def initialize(email_from, email_to, errors_dir = "#{RAILS_ROOT}/log/errors")
|
77
|
+
FileUtils.mkdir_p(errors_dir)
|
78
|
+
@errors_dir = File.new(errors_dir)
|
79
|
+
@email_from = email_from
|
80
|
+
@email_to = email_to
|
81
|
+
end
|
82
|
+
|
83
|
+
def dump(controller, exception)
|
84
|
+
time = Time.now
|
85
|
+
error_log_id = "#{time.tv_sec}#{time.tv_usec}"
|
86
|
+
|
87
|
+
log_file = File.join(@errors_dir.path.to_s, "error-#{error_log_id}.log")
|
88
|
+
logger = Logger.new(log_file)
|
89
|
+
|
90
|
+
dump_environment(logger, exception, controller)
|
91
|
+
ErrorNotifier.deliver_error_notification(@email_from, @email_to, exception, log_file) unless @email_to.blank?
|
92
|
+
log_file
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def dump_environment_header(logger, header)
|
98
|
+
logger.error("\n===================================================================\n#{header}\n===================================================================\n");
|
99
|
+
end
|
100
|
+
|
101
|
+
def dump_environment(logger, exception, controller)
|
102
|
+
dump_environment_header(logger, "REQUEST DUMP");
|
103
|
+
dump_hashmap(logger, controller.request.env)
|
104
|
+
|
105
|
+
dump_environment_header(logger, "RESPONSE DUMP");
|
106
|
+
dump_hashmap(logger, controller.response.headers)
|
107
|
+
|
108
|
+
dump_environment_header(logger, "SESSION DUMP");
|
109
|
+
dump_hashmap(logger, controller.session)
|
110
|
+
|
111
|
+
dump_environment_header(logger, "PARAMETER DUMP");
|
112
|
+
map = {}
|
113
|
+
dump_hashmap(logger, controller.params.each{ |k,v| map[k]=v })
|
114
|
+
|
115
|
+
dump_environment_header(logger, "EXCEPTION");
|
116
|
+
logger.error("#{exception.class}:#{exception.message}")
|
117
|
+
logger.error("\t" + exception.backtrace.join("\n\t"))
|
118
|
+
end
|
119
|
+
|
120
|
+
def dump_hashmap(logger, map)
|
121
|
+
for key,value in map
|
122
|
+
logger.error("\t#{key} => #{value.inspect}")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
ActionController::Base.send(:include, Ixtlan::Rails::ErrorHandling)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'ixtlan/guard'
|
2
|
+
|
3
|
+
ActionController::Base.send(:include, Ixtlan::ActionController::Guard)
|
4
|
+
ActionController::Base.send(:before_filter, :guard)
|
5
|
+
ActionView::Base.send(:include, Ixtlan::Allowed)
|
6
|
+
module Erector
|
7
|
+
class Widget
|
8
|
+
include Ixtlan::Allowed
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Rails
|
3
|
+
module SessionTimeout
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module InstanceMethods
|
10
|
+
private
|
11
|
+
|
12
|
+
CONFIG = Object.full_const_get(Ixtlan::Models::CONFIGURATION)
|
13
|
+
|
14
|
+
def session_user_logger
|
15
|
+
@session_user_logger ||= UserLogger.new(Ixtlan::Rails::SessionTimeout)
|
16
|
+
end
|
17
|
+
|
18
|
+
def expire_session
|
19
|
+
session.clear
|
20
|
+
# reset_session
|
21
|
+
render_session_timeout
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
def check_session_expiry
|
28
|
+
if !session[:expires_at].nil? and session[:expires_at] < DateTime.now
|
29
|
+
# Session has expired.
|
30
|
+
session_user_logger.log(self, "session timeout")
|
31
|
+
expire_session
|
32
|
+
else
|
33
|
+
# Assign a new expiry time
|
34
|
+
session[:expires_at] = session_timeout.minutes.from_now
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_session_ip_binding
|
40
|
+
if !session[:session_ip].nil? and session[:session_ip] != request.headers['REMOTE_ADDR']
|
41
|
+
# client IP has changed
|
42
|
+
session_user_logger.log(self, "IP changed from #{session[:session_ip]} to #{request.headers['REMOTE_ADDR']}")
|
43
|
+
expire_session
|
44
|
+
else
|
45
|
+
# Assign client IP
|
46
|
+
session[:session_ip] = request.headers['REMOTE_ADDR']
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def check_session
|
52
|
+
check_session_ip_binding and check_session_expiry
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_session_browser_signature
|
56
|
+
if !session[:session_browser_signature].nil? and session[:session_browser_signature] != retrieve_browser_signature
|
57
|
+
# browser signature has changed
|
58
|
+
session_user_logger.log(self, "browser signature changed from #{session[:session_browser_signature]} to #{retrieve_browser_signature}")
|
59
|
+
expire_session
|
60
|
+
return false
|
61
|
+
else
|
62
|
+
# Assign a browser signature
|
63
|
+
session[:session_browser_signature] = retrieve_browser_signature
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def retrieve_browser_signature
|
69
|
+
[request.headers['HTTP_USER_AGENT'],
|
70
|
+
request.headers['HTTP_ACCEPT_LANGUAGE'],
|
71
|
+
request.headers['HTTP_ACCEPT_ENCODING'],
|
72
|
+
request.headers['HTTP_ACCEPT']].join('|')
|
73
|
+
end
|
74
|
+
|
75
|
+
def render_session_timeout
|
76
|
+
respond_to do |format|
|
77
|
+
format.html {
|
78
|
+
@notice = "session timeout" unless @notice
|
79
|
+
render :template => "sessions/login"
|
80
|
+
}
|
81
|
+
format.xml { head :unauthorized }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def session_timeout
|
86
|
+
CONFIG.instance.session_idle_timeout
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
ActionController::Base.send(:include, Ixtlan::Rails::SessionTimeout)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Rails
|
3
|
+
module TimestampsModifiedBy
|
4
|
+
module Base
|
5
|
+
def self.included(base)
|
6
|
+
base.prepend_around_filter(Filter)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Filter
|
11
|
+
def self.filter(controller)
|
12
|
+
name = controller.params[:controller]
|
13
|
+
unless name.nil?
|
14
|
+
parameters = controller.params[name.singular.to_sym]
|
15
|
+
unless parameters.nil?
|
16
|
+
parameters.delete(:created_at)
|
17
|
+
parameters.delete(:created_on)
|
18
|
+
parameters.delete(:created_by)
|
19
|
+
# do not delete the updated_at so that optimistic persistence
|
20
|
+
# can work !!!!
|
21
|
+
#parameters.delete(:updated_at)
|
22
|
+
parameters.delete(:updated_on)
|
23
|
+
parameters.delete(:updated_by)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
yield if block_given?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
::ActionController::Base.send(:include, Ixtlan::Rails::TimestampsModifiedBy::Base)
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Ixtlan
|
2
|
+
module Rails
|
3
|
+
module UnrestfulAuthentication
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
USER = Object.full_const_get(Ixtlan::Models::USER)
|
8
|
+
|
9
|
+
AUTHENTICATION = Object.full_const_get(Ixtlan::Models::AUTHENTICATION)
|
10
|
+
|
11
|
+
def authentication_logger
|
12
|
+
@authentication_logger ||= UserLogger.new(Ixtlan::Rails::UnrestfulAuthentication)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def logged_in?
|
18
|
+
!session[:user_id].nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def current_user
|
22
|
+
session[:user] = login_from_session if logged_in? and session[:user].nil?
|
23
|
+
session[:user]
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_user=(new_user)
|
27
|
+
session[:user_id] = new_user ? new_user.id : nil
|
28
|
+
session[:user] = new_user
|
29
|
+
end
|
30
|
+
|
31
|
+
def verify_authenticity
|
32
|
+
if request.content_type == 'application/xml'
|
33
|
+
params[request_forgery_protection_token] = request.headers[:authenticity_token]
|
34
|
+
end
|
35
|
+
verify_authenticity_token if logged_in?
|
36
|
+
end
|
37
|
+
|
38
|
+
def authenticate
|
39
|
+
if logged_in?
|
40
|
+
case(request.method)
|
41
|
+
when :delete
|
42
|
+
logout
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
else
|
47
|
+
case(request.method)
|
48
|
+
when :get
|
49
|
+
session.clear
|
50
|
+
render_login_page
|
51
|
+
when :post
|
52
|
+
user = login_from_params
|
53
|
+
if user.instance_of? String
|
54
|
+
authentication_logger.log_user(params[:login], user + " from IP #{request.headers['REMOTE_ADDR']}")
|
55
|
+
session.clear
|
56
|
+
render_access_denied
|
57
|
+
else
|
58
|
+
authentication_logger.log_user(user.login, "logged in")
|
59
|
+
session.clear
|
60
|
+
# reset_session
|
61
|
+
self.current_user = user
|
62
|
+
render_successful_login
|
63
|
+
end
|
64
|
+
else
|
65
|
+
session.clear
|
66
|
+
render_access_denied
|
67
|
+
end
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def login_from_params
|
73
|
+
USER.authenticate(params[:login], params[:password])
|
74
|
+
end
|
75
|
+
|
76
|
+
def login_from_session
|
77
|
+
USER.get(session[:user_id])
|
78
|
+
end
|
79
|
+
|
80
|
+
def logout
|
81
|
+
if(params[:login] == current_user.login or request.content_type == 'application/xml')
|
82
|
+
authentication_logger.log_user(current_user.login, "logged out")
|
83
|
+
current_user = nil
|
84
|
+
session.clear
|
85
|
+
# reset_session
|
86
|
+
render_logout_page
|
87
|
+
false
|
88
|
+
else
|
89
|
+
true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def render_successful_login
|
94
|
+
respond_to do |format|
|
95
|
+
format.html { redirect_to request.url, :status => :moved_permanently}
|
96
|
+
format.xml do
|
97
|
+
authentication = AUTHENTICATION.new
|
98
|
+
authentication.login = self.current_user.login
|
99
|
+
authentication.user = self.current_user
|
100
|
+
authentication.token = form_authenticity_token
|
101
|
+
render :xml => authentication.to_xml
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def render_access_denied
|
107
|
+
respond_to do |format|
|
108
|
+
format.html do
|
109
|
+
@notice = "access denied" unless @notice
|
110
|
+
render :template => "sessions/login", :status => :unauthorized
|
111
|
+
end
|
112
|
+
format.xml { head :unauthorized}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def render_login_page
|
117
|
+
respond_to do |format|
|
118
|
+
format.html { render :template => "sessions/login" }
|
119
|
+
format.xml { head :unauthorized }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def render_logout_page
|
124
|
+
respond_to do |format|
|
125
|
+
format.html do
|
126
|
+
@notice = "logged out" unless @notice
|
127
|
+
render :template => "sessions/login"
|
128
|
+
end
|
129
|
+
format.xml { head :ok }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
ActionController::Base.send(:include, Ixtlan::Rails::UnrestfulAuthentication)
|
137
|
+
ActionController::Base.send(:prepend_before_filter, :authenticate)
|