aker 3.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. data/CHANGELOG.md +210 -0
  2. data/README.md +282 -0
  3. data/assets/aker/form/login.css +73 -0
  4. data/assets/aker/form/login.html.erb +44 -0
  5. data/lib/aker/authorities/automatic_access.rb +36 -0
  6. data/lib/aker/authorities/composite.rb +301 -0
  7. data/lib/aker/authorities/static.rb +283 -0
  8. data/lib/aker/authorities/support/find_sole_user.rb +24 -0
  9. data/lib/aker/authorities/support.rb +9 -0
  10. data/lib/aker/authorities.rb +46 -0
  11. data/lib/aker/cas/authority.rb +79 -0
  12. data/lib/aker/cas/configuration_helper.rb +85 -0
  13. data/lib/aker/cas/middleware/logout_responder.rb +49 -0
  14. data/lib/aker/cas/middleware/ticket_remover.rb +35 -0
  15. data/lib/aker/cas/middleware.rb +6 -0
  16. data/lib/aker/cas/proxy_mode.rb +108 -0
  17. data/lib/aker/cas/rack_proxy_callback.rb +188 -0
  18. data/lib/aker/cas/service_mode.rb +88 -0
  19. data/lib/aker/cas/service_url.rb +62 -0
  20. data/lib/aker/cas/user_ext.rb +64 -0
  21. data/lib/aker/cas.rb +31 -0
  22. data/lib/aker/central_parameters.rb +101 -0
  23. data/lib/aker/configuration.rb +534 -0
  24. data/lib/aker/deprecation.rb +105 -0
  25. data/lib/aker/form/custom_views_mode.rb +80 -0
  26. data/lib/aker/form/login_form_asset_provider.rb +56 -0
  27. data/lib/aker/form/middleware/custom_view_login_responder.rb +19 -0
  28. data/lib/aker/form/middleware/login_renderer.rb +72 -0
  29. data/lib/aker/form/middleware/login_responder.rb +71 -0
  30. data/lib/aker/form/middleware/logout_responder.rb +26 -0
  31. data/lib/aker/form/middleware.rb +10 -0
  32. data/lib/aker/form/mode.rb +118 -0
  33. data/lib/aker/form.rb +26 -0
  34. data/lib/aker/group.rb +67 -0
  35. data/lib/aker/group_membership.rb +162 -0
  36. data/lib/aker/ldap/authority.rb +392 -0
  37. data/lib/aker/ldap/user_ext.rb +19 -0
  38. data/lib/aker/ldap.rb +22 -0
  39. data/lib/aker/modes/base.rb +85 -0
  40. data/lib/aker/modes/http_basic.rb +100 -0
  41. data/lib/aker/modes/support/attempted_path.rb +22 -0
  42. data/lib/aker/modes/support/rfc_2617.rb +32 -0
  43. data/lib/aker/modes/support.rb +12 -0
  44. data/lib/aker/modes.rb +48 -0
  45. data/lib/aker/rack/authenticate.rb +37 -0
  46. data/lib/aker/rack/configuration_helper.rb +18 -0
  47. data/lib/aker/rack/default_logout_responder.rb +36 -0
  48. data/lib/aker/rack/environment_helper.rb +34 -0
  49. data/lib/aker/rack/facade.rb +102 -0
  50. data/lib/aker/rack/failure.rb +69 -0
  51. data/lib/aker/rack/logout.rb +63 -0
  52. data/lib/aker/rack/request_ext.rb +19 -0
  53. data/lib/aker/rack/session_timer.rb +95 -0
  54. data/lib/aker/rack/setup.rb +77 -0
  55. data/lib/aker/rack.rb +107 -0
  56. data/lib/aker/test/helpers.rb +22 -0
  57. data/lib/aker/test.rb +8 -0
  58. data/lib/aker/user.rb +231 -0
  59. data/lib/aker/version.rb +3 -0
  60. data/lib/aker.rb +51 -0
  61. data/spec/aker/aker-sample.yml +11 -0
  62. data/spec/aker/authorities/automatic_access_spec.rb +52 -0
  63. data/spec/aker/authorities/composite_spec.rb +488 -0
  64. data/spec/aker/authorities/nu-schema.jar +0 -0
  65. data/spec/aker/authorities/static_spec.rb +455 -0
  66. data/spec/aker/authorities/support/find_sole_user_spec.rb +33 -0
  67. data/spec/aker/authorities_spec.rb +16 -0
  68. data/spec/aker/cas/authority_spec.rb +106 -0
  69. data/spec/aker/cas/configuration_helper_spec.rb +92 -0
  70. data/spec/aker/cas/middleware/logout_responder_spec.rb +47 -0
  71. data/spec/aker/cas/middleware/ticket_remover_spec.rb +49 -0
  72. data/spec/aker/cas/proxy_mode_spec.rb +185 -0
  73. data/spec/aker/cas/rack_proxy_callback_spec.rb +190 -0
  74. data/spec/aker/cas/service_mode_spec.rb +122 -0
  75. data/spec/aker/cas/service_url_spec.rb +114 -0
  76. data/spec/aker/cas/user_ext_spec.rb +27 -0
  77. data/spec/aker/cas_spec.rb +19 -0
  78. data/spec/aker/central_parameters_spec.rb +44 -0
  79. data/spec/aker/configuration_spec.rb +465 -0
  80. data/spec/aker/deprecation_spec.rb +115 -0
  81. data/spec/aker/form/a_form_mode.rb +129 -0
  82. data/spec/aker/form/custom_views_mode_spec.rb +34 -0
  83. data/spec/aker/form/login_form_asset_provider_spec.rb +80 -0
  84. data/spec/aker/form/middleware/a_form_login_responder.rb +89 -0
  85. data/spec/aker/form/middleware/custom_view_login_responder_spec.rb +47 -0
  86. data/spec/aker/form/middleware/login_renderer_spec.rb +56 -0
  87. data/spec/aker/form/middleware/login_responder_spec.rb +34 -0
  88. data/spec/aker/form/middleware/logout_responder_spec.rb +55 -0
  89. data/spec/aker/form/mode_spec.rb +15 -0
  90. data/spec/aker/form_spec.rb +11 -0
  91. data/spec/aker/group_membership_spec.rb +208 -0
  92. data/spec/aker/group_spec.rb +66 -0
  93. data/spec/aker/ldap/authority_spec.rb +414 -0
  94. data/spec/aker/ldap/ldap-users.ldif +197 -0
  95. data/spec/aker/ldap_spec.rb +11 -0
  96. data/spec/aker/modes/a_aker_mode.rb +41 -0
  97. data/spec/aker/modes/http_basic_spec.rb +127 -0
  98. data/spec/aker/modes/support/attempted_path_spec.rb +32 -0
  99. data/spec/aker/modes_spec.rb +11 -0
  100. data/spec/aker/rack/authenticate_spec.rb +78 -0
  101. data/spec/aker/rack/default_logout_responder_spec.rb +67 -0
  102. data/spec/aker/rack/facade_spec.rb +154 -0
  103. data/spec/aker/rack/failure_spec.rb +151 -0
  104. data/spec/aker/rack/logout_spec.rb +63 -0
  105. data/spec/aker/rack/request_ext_spec.rb +29 -0
  106. data/spec/aker/rack/session_timer_spec.rb +134 -0
  107. data/spec/aker/rack/setup_spec.rb +87 -0
  108. data/spec/aker/rack_spec.rb +216 -0
  109. data/spec/aker/test/helpers_spec.rb +44 -0
  110. data/spec/aker/user_spec.rb +362 -0
  111. data/spec/aker_spec.rb +80 -0
  112. data/spec/deprecation_helper.rb +58 -0
  113. data/spec/java_helper.rb +5 -0
  114. data/spec/logger_helper.rb +17 -0
  115. data/spec/matchers.rb +31 -0
  116. data/spec/mock_builder.rb +25 -0
  117. data/spec/spec_helper.rb +52 -0
  118. metadata +265 -0
data/lib/aker/modes.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'aker'
2
+
3
+ module Aker
4
+ ##
5
+ # Mode support code.
6
+ #
7
+ # A mode implements an authentication protocol, and is classified as a _UI
8
+ # mode_, an _API mode_, or both. UI modes are intended for interactive use;
9
+ # API modes are intended for non-interactive use.
10
+ #
11
+ # Aker ships with five modes:
12
+ #
13
+ # - {Aker::Cas::ServiceMode :cas} is a UI mode that provides interactive login via
14
+ # a CAS server.
15
+ # - {Aker::Cas::ProxyMode :cas_proxy} is an API mode that implements the
16
+ # CAS proxying protocol.
17
+ # - {Aker::Form::Mode :form} is a UI mode that provides an HTML form that
18
+ # prompts for username and password.
19
+ # - {Aker::Form::CustomViewsMode :custom_form} is a specialization
20
+ # of `:form` for apps that wish to provide their own form views.
21
+ # - {Aker::Modes::HttpBasic :http_basic} is an API/UI mode that implements
22
+ # the HTTP Basic authentication protocol. (It's both an API and UI mode
23
+ # because it can be used by automated Web clients and humans alike.)
24
+ #
25
+ # Aker permits applications to use as many API modes as they wish,
26
+ # but requires that applications have one and only one UI mode. The
27
+ # default UI mode is `:form`.
28
+ #
29
+ # @see Aker::Configuration#ui_mode=
30
+ # @see Aker::Configuration#api_modes=
31
+ module Modes
32
+ autoload :Base, 'aker/modes/base'
33
+ autoload :HttpBasic, 'aker/modes/http_basic'
34
+ autoload :Support, 'aker/modes/support'
35
+
36
+ ##
37
+ # @private
38
+ class Slice < Aker::Configuration::Slice
39
+ def initialize
40
+ super do
41
+ register_mode Aker::Modes::HttpBasic
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ Aker::Configuration.add_default_slice(Aker::Modes::Slice.new)
@@ -0,0 +1,37 @@
1
+ require 'aker'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # The middleware which actually performs authentication according to
6
+ # the mode that applies to the request (if any). Most of the heavy
7
+ # lifting is performed by Warden.
8
+ class Authenticate
9
+ include EnvironmentHelper
10
+
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ ##
16
+ # Authenticates incoming requests using Warden.
17
+ #
18
+ # Additionally, this class exposes the `aker.check` environment
19
+ # variable to downstream middleware and the app. It is an
20
+ # instance of {Aker::Rack::Facade} permitting authentication and
21
+ # authorization queries about the current user (if any).
22
+ def call(env)
23
+ configuration = configuration(env)
24
+ warden = env['warden']
25
+
26
+ if interactive?(env)
27
+ warden.authenticate(configuration.ui_mode)
28
+ else
29
+ warden.authenticate(*configuration.api_modes)
30
+ end
31
+
32
+ env['aker.check'] = Facade.new(configuration, warden.user)
33
+
34
+ @app.call(env)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require 'aker'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # Methods used by Rack middleware for reading configuration data out of the
6
+ # Rack environment.
7
+ module ConfigurationHelper
8
+ include EnvironmentHelper
9
+
10
+ def login_path(env)
11
+ configuration(env).parameters_for(:rack)[:login_path]
12
+ end
13
+
14
+ def logout_path(env)
15
+ configuration(env).parameters_for(:rack)[:logout_path]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ require 'aker'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # Provides a default response for `GET` of the application's
6
+ # configured logout path.
7
+ class DefaultLogoutResponder
8
+ include ConfigurationHelper
9
+
10
+ ##
11
+ # Instantiates the middleware.
12
+ #
13
+ # @param app [Rack app] the Rack application on which this middleware
14
+ # should be layered
15
+ def initialize(app)
16
+ @app = app
17
+ end
18
+
19
+ ##
20
+ # When the path is the configured logout path, renders a logout
21
+ # response.
22
+ #
23
+ # @param env [Hash] a Rack environment
24
+ def call(env)
25
+ result = @app.call(env)
26
+
27
+ if result.first == 404 &&
28
+ env['REQUEST_METHOD'] == 'GET' &&
29
+ env['PATH_INFO'] == logout_path(env)
30
+ ::Rack::Response.new('You have been logged out.', 200).finish
31
+ else
32
+ result
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ require 'aker'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # Methods used by Rack middleware for reading Aker data out of the Rack
6
+ # environment.
7
+ module EnvironmentHelper
8
+ ##
9
+ # Returns the {Configuration} instance for the current request.
10
+ #
11
+ # @return [Aker::Configuration]
12
+ def configuration(env)
13
+ env['aker.configuration']
14
+ end
15
+
16
+ ##
17
+ # Whether the current request is interactive.
18
+ #
19
+ # @see Aker::Rack::Setup#call
20
+ # @see Aker::Rack::Setup#interactive?
21
+ # @return [Boolean]
22
+ def interactive?(env)
23
+ env['aker.interactive']
24
+ end
25
+
26
+ ##
27
+ # The authority to use for credential validation.
28
+ #
29
+ # @return [Object]
30
+ def authority(env)
31
+ env['aker.authority']
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,102 @@
1
+ require 'aker/rack'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # Provides a simple interface which aker-using rack apps may use to
6
+ # indicate that authentication or authorization is required for a
7
+ # particular action.
8
+ #
9
+ # An instance of this class is available in the rack environment
10
+ # under the `"aker"` key.
11
+ class Facade
12
+ ##
13
+ # The current authenticated user.
14
+ #
15
+ # @return [Aker::User]
16
+ attr_accessor :user
17
+
18
+ ##
19
+ # The aker configuration in effect for this application.
20
+ #
21
+ # @return [Aker::Configuration]
22
+ attr_accessor :configuration
23
+
24
+ def initialize(config, user)
25
+ @configuration = config
26
+ @user = user
27
+ end
28
+
29
+ ##
30
+ # Indicates that authentication is required for a particular
31
+ # request. If the user is not authenticated, any application code
32
+ # after this method is called will not be executed. The user will
33
+ # be directed to authenticate according to their access style
34
+ # (ui vs. api) and the application configuration (i.e., the
35
+ # appropriate {Modes mode}).
36
+ #
37
+ # If the application has a {Aker::Configuration#portal portal}
38
+ # configured, aker will also check that the user has access to
39
+ # that portal. If the user is authenticated but does not have
40
+ # access to the portal, she will get a `403 Forbidden` response.
41
+ #
42
+ # @see #authenticated?
43
+ # @return [void]
44
+ def authentication_required!
45
+ throw :warden, inauthentic_reason unless authenticated?
46
+ end
47
+
48
+ ##
49
+ # Returns true if there is an authenticated user, false otherwise.
50
+ # This check follows the same rules as
51
+ # {#authentication_required!}, including the portal check.
52
+ # However, it does not halt processing if the user is not
53
+ # authenticated.
54
+ #
55
+ # @return [Boolean]
56
+ def authenticated?
57
+ inauthentic_reason.nil?
58
+ end
59
+
60
+ ##
61
+ # A shortcut to invoking {Aker::User#permit?} on the {#user
62
+ # current user}. As with that method, the block is optional.
63
+ #
64
+ # This method safely handles the case where there is no user
65
+ # logged in.
66
+ #
67
+ # @param [Array<#to_sym>] groups
68
+ # @return [Boolean, Object, nil] `nil` if there's no one logged in;
69
+ # otherwise the same as {Aker::User#permit?}.
70
+ def permit?(*groups, &block)
71
+ return nil unless user
72
+ user.permit?(*groups, &block)
73
+ end
74
+ alias :permit :permit?
75
+
76
+ ##
77
+ # Indicates that a user must be in one of the specified groups to
78
+ # proceed. If there is a user logged in and she is not in any of
79
+ # the specified groups, she will get a `403 Forbidden` response.
80
+ # If the user is not logged in, she will be prompted to log in
81
+ # (just like with {#authentication_required!}).
82
+ #
83
+ # @return [void]
84
+ def permit!(*groups)
85
+ authentication_required!
86
+ throw :warden, :groups_required => groups unless user.permit?(*groups)
87
+ end
88
+
89
+ private
90
+
91
+ def inauthentic_reason
92
+ @inauthentic_reason =
93
+ if !user
94
+ { :login_required => true }
95
+ elsif !configuration.portal? || user.may_access?(configuration.portal)
96
+ nil
97
+ else
98
+ { :portal_required => configuration.portal }
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,69 @@
1
+ require 'aker/rack'
2
+ require 'warden'
3
+
4
+ module Aker::Rack
5
+ ##
6
+ # The Rack endpoint which handles authentication failures.
7
+ #
8
+ # @see Aker::Rack.use_in
9
+ # @see http://wiki.github.com/hassox/warden/failures
10
+ # Warden failures documentation
11
+ class Failure
12
+ include EnvironmentHelper
13
+
14
+ ##
15
+ # Receives the rack environment in case of a failure and renders a
16
+ # response based on the interactiveness of the request and the
17
+ # nature of the configured modes.
18
+ #
19
+ # @param [Hash] env a rack environment
20
+ #
21
+ # @return [Array] a rack response
22
+ def call(env)
23
+ conf = configuration(env)
24
+ if login_required?(env)
25
+ if interactive?(env)
26
+ ::Warden::Strategies[conf.ui_mode].new(env).on_ui_failure.finish
27
+ else
28
+ headers = {}
29
+ headers["WWW-Authenticate"] =
30
+ conf.api_modes.collect { |mode_key|
31
+ ::Warden::Strategies[mode_key].new(env).challenge
32
+ }.join("\n")
33
+ headers["Content-Type"] = "text/plain"
34
+ [401, headers, ["Authentication required"]]
35
+ end
36
+ else
37
+ log_authorization_failure(env)
38
+ msg = "#{user(env).username} may not use this page."
39
+ Rack::Response.
40
+ new("<html><head><title>Authorization denied</title></head><body>#{msg}</body></html>",
41
+ 403,
42
+ "Content-Type" => "text/html").finish
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def login_required?(env)
49
+ env['warden.options'][:login_required]
50
+ end
51
+
52
+ def user(env)
53
+ env['aker.check'].user
54
+ end
55
+
56
+ def log_authorization_failure(env)
57
+ wo = env['warden.options']
58
+ msg = "Resource authorization failure: User \"#{user(env).username}\" " <<
59
+ if wo[:portal_required]
60
+ "is not in the #{wo[:portal_required].inspect} portal."
61
+ elsif wo[:groups_required]
62
+ "is not in any of the required groups #{wo[:groups_required].inspect}."
63
+ else
64
+ "is just generally unauthorized. Don't ask questions."
65
+ end
66
+ configuration(env).logger.info(msg)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,63 @@
1
+ require 'aker'
2
+
3
+ module Aker
4
+ module Rack
5
+ ##
6
+ # Middleware for ending authenticated sessions. This middleware
7
+ # listens for `GET` requests to the logout path and when such
8
+ # requests are received, clears user data.
9
+ #
10
+ # The logout path is `/logout` by default. It may be overridden in
11
+ # the Aker configuration by setting a value for `:logout_path` in
12
+ # the `:rack` parameter group.
13
+ #
14
+ # ## Implications of GET
15
+ #
16
+ # `GET` was chosen to ensure that there always exists a way to clear
17
+ # application session data independent of whether it is possible to get to a
18
+ # logout link. (If unmarshalable data exists in the session -- say, stored
19
+ # objects whose format has changed between application revisions -- it is
20
+ # possible to get into a state where logout links cannot be accessed.)
21
+ #
22
+ # Using `GET` does mean that it is possible to execute CSRF attacks that
23
+ # will log out the user. The severity of this can range from a minor
24
+ # annoyance (just having to log in again while browsing a series of pages)
25
+ # to major (losing all data in a large POST).
26
+ #
27
+ # @see Aker::Rack.use_in
28
+ #
29
+ # @author David Yip
30
+ class Logout
31
+ include ConfigurationHelper
32
+
33
+ ##
34
+ # Instantiates the middleware.
35
+ #
36
+ # @param app [Rack app] the Rack application on which this middleware
37
+ # should be layered
38
+ def initialize(app)
39
+ @app = app
40
+ end
41
+
42
+ ##
43
+ # When given a `GET` for the configured logout path, invokes
44
+ # Warden's logout procedure (which resets the session), and
45
+ # passes control down to the rest of the application.
46
+ #
47
+ # If the application or a mode does not provide a handler for
48
+ # the configured logout path, then the handler defined by
49
+ # {DefaultLogoutResponder} will be invoked.
50
+ #
51
+ # @see Aker::Rack.use_in
52
+ # @param env [Hash] a Rack environment
53
+ # @return [Array] a finished Rack response
54
+ def call(env)
55
+ if env['REQUEST_METHOD'] == 'GET' && env['PATH_INFO'] == logout_path(env)
56
+ env['warden'].logout
57
+ end
58
+
59
+ @app.call(env)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ require 'aker'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # Extensions for `Rack::Request`.
6
+ #
7
+ # To use these, `include` them into `Rack::Request`.
8
+ module RequestExt
9
+ include Aker::Rack::EnvironmentHelper
10
+
11
+ ##
12
+ # Whether the current request is interactive.
13
+ #
14
+ # @return [Boolean]
15
+ def interactive?
16
+ super(env)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,95 @@
1
+ require 'aker'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # Middleware that permits a Web application to enforce a session inactivity
6
+ # limit. When a request is made after the session expires, the
7
+ # middleware resets the session, forcing the user to be reauthenticated.
8
+ #
9
+ # The session inactivity limit is determined by the `session-timeout-seconds`
10
+ # parameter in Aker's `policy` parameter group. It defaults to 1800 seconds
11
+ # (30 minutes), and can be overridden by a {Aker::ConfiguratorLanguage Aker
12
+ # configuration block} or {Aker::CentralParameters central parameters file}.
13
+ # To disable session timeout, set `session-timeout-seconds` to `nil` or `0`.
14
+ #
15
+ # Algorithm
16
+ # =========
17
+ #
18
+ # On each request:
19
+ #
20
+ # let lr = timestamp of last request,
21
+ # cr = timestamp of current request,
22
+ # st = session timeout from configuration,
23
+ # ta = lr + st
24
+ #
25
+ # if st is nil
26
+ # pass control to rest of application
27
+ # end
28
+ #
29
+ # store ta in the Rack environment as aker.timeout_at
30
+ # lr := cr
31
+ # store lr in the session
32
+ #
33
+ # if lr is nil
34
+ # pass control to rest of application
35
+ # end
36
+ #
37
+ # if cr is in [lr, ta]
38
+ # pass control to rest of application
39
+ # else
40
+ # reset session
41
+ # pass control to rest of application
42
+ # end
43
+ #
44
+ #
45
+ # Requirements
46
+ # ============
47
+ #
48
+ # SessionTimer expects a session manager that behaves like a `Rack::Session`
49
+ # session manager to be present in the `rack.session` Rack environment
50
+ # variable.
51
+ #
52
+ # SessionTimer should be configured earlier in the middleware stack
53
+ # than any middleware which checks for cached
54
+ # credentials. {Aker::Rack.use_in} arranges things this way.
55
+ class SessionTimer
56
+ include EnvironmentHelper
57
+ include ConfigurationHelper
58
+
59
+ def initialize(app)
60
+ @app = app
61
+ end
62
+
63
+ ##
64
+ # Determines whether the incoming request arrived within the timeout
65
+ # window. If it did, then the request is passed onto the rest of the Rack
66
+ # stack; otherwise, the user is redirected to the configured
67
+ # logout path.
68
+ #
69
+ def call(env)
70
+ now = Time.now.to_i
71
+ session = env['rack.session']
72
+ window_size = window_size(env)
73
+ previous_timeout = session['aker.last_request_at']
74
+
75
+ return @app.call(env) unless window_size > 0
76
+
77
+ env['aker.timeout_at'] = now + window_size
78
+ session['aker.last_request_at'] = now
79
+
80
+ return @app.call(env) unless previous_timeout
81
+
82
+ if now >= previous_timeout + window_size
83
+ env['aker.session_expired'] = true
84
+ env['warden'].logout
85
+ end
86
+ @app.call(env)
87
+ end
88
+
89
+ private
90
+
91
+ def window_size(env)
92
+ configuration(env).parameters_for(:policy)[%s(session-timeout-seconds)].to_i
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,77 @@
1
+ require 'aker/rack'
2
+
3
+ module Aker::Rack
4
+ ##
5
+ # The middleware which makes the aker environment available in the
6
+ # rake environment and authenticates the credentials that the
7
+ # request provides (if any). It is responsible for determining
8
+ # whether the request is interactive or not and will use the
9
+ # appropriate configured {Aker::Modes mode} based on this decision.
10
+ #
11
+ # You probably don't want to `use` this directly; use
12
+ # {Aker::Rack.use_in} to configure in this middleware and all its
13
+ # dependencies simultaneously.
14
+ #
15
+ # @see Aker::Rack.use_in
16
+ # @see Aker::Configuration#ui_mode
17
+ # @see Aker::Configuration#api_modes
18
+ class Setup
19
+ ##
20
+ # Creates a new instance of the middleware.
21
+ #
22
+ # @param [#call] app the application this middleware is being
23
+ # wrapped around.
24
+ # @param [Aker::Configuration] configuration the configuration to use for
25
+ # this instance.
26
+ #
27
+ # @see Aker::Rack.use_in
28
+ def initialize(app, configuration)
29
+ @app = app
30
+ @configuration = configuration
31
+ end
32
+
33
+ ##
34
+ # Implements the rack middleware behavior.
35
+ #
36
+ # This class exposes three environment variables to downstream
37
+ # middleware and the app:
38
+ #
39
+ # * `"aker.configuration"`: the {Aker::Configuration configuration}
40
+ # for this application.
41
+ # * `"aker.authority"`: the {Aker::Authorities authority} for
42
+ # this application.
43
+ # * `"aker.interactive"`: a boolean indicating whether this
44
+ # request is being treated as an interactive (UI) or
45
+ # non-interactive (API) request
46
+ #
47
+ # [There is a related fourth environment variable:
48
+ #
49
+ # * `"aker.check"`: an instance of {Aker::Rack::Facade}
50
+ # permitting authentication and authorization queries about the
51
+ # current user (if any).
52
+ #
53
+ # This fourth variable is added by the {Authenticate} middleware;
54
+ # see its documentation for more.]
55
+ #
56
+ # @param [Hash] env the rack env
57
+ # @return [Array] the standard rack return
58
+ def call(env)
59
+ env['aker.configuration'] = @configuration
60
+ env['aker.authority'] = @configuration.composite_authority
61
+ env['aker.interactive'] = interactive?(env)
62
+
63
+ @app.call(env)
64
+ end
65
+
66
+ ##
67
+ # Determines if the given rack env represents an interactive
68
+ # request.
69
+ #
70
+ # @return [Boolean, nil] true if interactive, false or nil otherwise
71
+ def interactive?(env)
72
+ @configuration.api_modes.empty? or
73
+ env["HTTP_ACCEPT"] =~ %r{text/html} or
74
+ env["HTTP_USER_AGENT"] =~ %r{Mozilla}
75
+ end
76
+ end
77
+ end