himari 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ac0941b925171e38c0862b97f8a0ad2256c9ac56cfc3d3dd9b59bca1508dc4e
4
- data.tar.gz: 37b2fc9399deca00071f07140c53aac84a8deb6169c9cb11f919d34388dae217
3
+ metadata.gz: 9ef5f3bac5f8a375751669378420729ab8dbe9ce40c1e1dec0fb5a3ac938b304
4
+ data.tar.gz: ed87e0922bc863813624c34d217f02fd9f06fec442fd01a8b0c1daf760b6ca48
5
5
  SHA512:
6
- metadata.gz: c0513156a600ffe7a0364ab2c7da45153702afce8c0fd82caf2c6f697529793570f61280abe50316ae5dd491c49908a084fe8d8341ef0a8882a4c5fee5eaf954
7
- data.tar.gz: 5b63f0a995ebc2be40440d02831c2bbb40d8928277b3e181ffc0d227546274af0d6fee26f26f4cea9dd90578095b8023c9830973e5d39205273e4a1c7f2633d0
6
+ metadata.gz: 97c69604496c88d5b6e0f38cf0693cdd07ee6dd15b73d12e724efcea058a2f44492fcbe57ed5ece878f36e7bc2631a028c1464f9bc17d33f519eb2f17b8ff576
7
+ data.tar.gz: 3a9fff6c67bec527df79527f9e436c5703d95e822e0b0d3bc4cc460e003205d238a9e8ea965f2fbb931a18389282dfa2b770e60b931eaadc82dd683b413f841a
data/lib/himari/app.rb CHANGED
@@ -127,6 +127,7 @@ module Himari
127
127
  authz = AuthorizationCode.make(
128
128
  client_id: decision.client.id,
129
129
  claims: decision.claims,
130
+ lifetime: decision.lifetime,
130
131
  )
131
132
 
132
133
  Himari::Services::OidcAuthorizationEndpoint.new(
@@ -141,7 +142,20 @@ module Himari
141
142
  end
142
143
  rescue Himari::Services::DownstreamAuthorization::ForbiddenError => e
143
144
  logger&.warn(Himari::LogLine.new('authorize: downstream forbidden', req: request_as_log, allowed: e.result.authz_result.allowed, err: e.class.inspect, result: e.as_log))
144
- halt 403, "Forbidden"
145
+
146
+ @notice = message_human = e.result.authz_result&.user_facing_message
147
+
148
+ case e.result.authz_result&.suggestion
149
+ when nil
150
+ # do nothing
151
+ when :reauthenticate
152
+ logger&.warn(Himari::LogLine.new('authorize: prompt login to reauthenticate', req: request_as_log, allowed: e.result.authz_result.allowed, err: e.class.inspect, result: e.as_log))
153
+ next erb(:login)
154
+ else
155
+ raise ArgumentError, "Unknown suggestion value for DownstreamAuthorization denial; #{e.as_log.inspect}"
156
+ end
157
+
158
+ halt(403, "Forbidden#{message_human ? "; #{message_human}" : nil}")
145
159
  end
146
160
 
147
161
  token_ep = proc do
@@ -182,9 +196,12 @@ module Himari
182
196
  end
183
197
 
184
198
  omniauth_callback = proc do
199
+ authhash = request.env['omniauth.auth']
200
+ next halt(400, 'Bad Request') unless authhash
201
+
185
202
  # do upstream auth
186
203
  authn = Himari::Services::UpstreamAuthentication.from_request(request).perform
187
- logger&.info(Himari::LogLine.new('authentication allowed', req: request_as_log, allowed: authn.authn_result.allowed, uid: request.env.fetch('omniauth.auth')[:uid], provider: request.env.fetch('omniauth.auth')[:provider], result: authn.as_log))
204
+ logger&.info(Himari::LogLine.new('authentication allowed', req: request_as_log, allowed: authn.authn_result.allowed, uid: authhash[:uid], provider: authhash[:provider], result: authn.as_log))
188
205
  raise unless authn.authn_result.allowed # sanity check
189
206
 
190
207
  given_back_to = request.env['omniauth.params']&.fetch('back_to', nil)
@@ -203,7 +220,8 @@ module Himari
203
220
  redirect back_to
204
221
  rescue Himari::Services::UpstreamAuthentication::UnauthorizedError => e
205
222
  logger&.warn(Himari::LogLine.new('authentication denied', req: request_as_log, err: e.class.inspect, allowed: e.result.authn_result.allowed, uid: request.env.fetch('omniauth.auth')[:uid], provider: request.env.fetch('omniauth.auth')[:provider], result: e.as_log))
206
- halt(401, 'Unauthorized')
223
+ message_human = e.result.authn_result&.user_facing_message
224
+ halt(401, "Unauthorized#{message_human ? "; #{message_human}" : nil}")
207
225
  end
208
226
  get '/auth/:provider/callback', &omniauth_callback
209
227
  post '/auth/:provider/callback', &omniauth_callback
@@ -10,17 +10,25 @@ module Himari
10
10
  nonce
11
11
  code_challenge
12
12
  code_challenge_method
13
+ created_at
14
+ lifetime
13
15
  expiry
14
16
  )
15
17
  AuthorizationCode = Struct.new(*authz_attrs, keyword_init: true) do
16
18
  def self.make(**kwargs)
17
19
  new(
18
20
  code: SecureRandom.urlsafe_base64(32),
19
- expiry: Time.now.to_i + 900,
21
+ created_at: Time.now.to_i,
20
22
  **kwargs,
21
23
  )
22
24
  end
23
25
 
26
+ alias _expiry_raw expiry
27
+ private :_expiry_raw
28
+ def expiry
29
+ self._expiry_raw || (self.expiry = created_at + (lifetime || 900))
30
+ end
31
+
24
32
  def valid_redirect_uri?(given_uri)
25
33
  redirect_uri == given_uri
26
34
  end
@@ -59,6 +67,8 @@ module Himari
59
67
  claims: claims,
60
68
  nonce: nonce,
61
69
  openid: openid,
70
+ created_at: created_at.to_i,
71
+ lifetime: lifetime.to_i,
62
72
  expiry: expiry.to_i,
63
73
  pkce: pkce?,
64
74
  pkce_method: code_challenge_method,
@@ -76,6 +86,8 @@ module Himari
76
86
  nonce: nonce,
77
87
  code_challenge: code_challenge,
78
88
  code_challenge_method: code_challenge_method,
89
+ created_at: created_at.to_i,
90
+ lifetime: lifetime.to_i,
79
91
  expiry: expiry.to_i,
80
92
  }
81
93
  end
@@ -19,14 +19,15 @@ module Himari
19
19
 
20
20
  allow_effects(:allow, :deny, :continue, :skip)
21
21
 
22
- def initialize(claims: {}, allowed_claims: DEFAULT_ALLOWED_CLAIMS, lifetime: 3600 * 12)
22
+ def initialize(claims: {}, allowed_claims: DEFAULT_ALLOWED_CLAIMS, lifetime: 3600)
23
23
  super()
24
24
  @claims = claims
25
25
  @allowed_claims = allowed_claims
26
26
  @lifetime = lifetime
27
27
  end
28
28
 
29
- attr_reader :claims, :allowed_claims, :lifetime
29
+ attr_reader :claims, :allowed_claims
30
+ attr_accessor :lifetime
30
31
 
31
32
  def to_evolve_args
32
33
  {
@@ -37,10 +38,10 @@ module Himari
37
38
  end
38
39
 
39
40
  def as_log
40
- to_h.merge(claims: output, lifetime: @lifetime&.to_i)
41
+ to_h.merge(claims: output_claims, lifetime: @lifetime&.to_i)
41
42
  end
42
43
 
43
- def output
44
+ def output_claims
44
45
  claims.select { |k,_v| allowed_claims.include?(k) }
45
46
  end
46
47
  end
@@ -18,7 +18,7 @@ module Himari
18
18
  raise "#{self.class.name}.valid_effects is missing [BUG]" unless self.class.valid_effects
19
19
  end
20
20
 
21
- attr_reader :effect, :effect_comment, :rule_name
21
+ attr_reader :effect, :effect_comment, :effect_user_facing_message, :effect_suggestion, :rule_name
22
22
 
23
23
  def to_evolve_args
24
24
  raise NotImplementedError
@@ -29,7 +29,10 @@ module Himari
29
29
  rule_name: rule_name,
30
30
  effect: effect,
31
31
  effect_comment: effect_comment,
32
- }
32
+ }.tap do |x|
33
+ x[:effect_user_facing_message] = effect_user_facing_message if effect_user_facing_message
34
+ x[:effect_suggestion] = effect_suggestion if effect_suggestion
35
+ end
33
36
  end
34
37
 
35
38
  def as_log
@@ -46,18 +49,21 @@ module Himari
46
49
  self
47
50
  end
48
51
 
49
- def decide!(effect, comment = "")
52
+ def decide!(effect, comment = "", user_facing_message: nil, suggest: nil)
50
53
  raise DecisionAlreadyMade, "decision can only be made once per rule (#{rule_name})" if @effect
51
54
  raise InvalidEffect, "this effect is not valid under this rule. Valid effects: #{self.class.valid_effects.inspect} (#{rule_name})" unless self.class.valid_effects.include?(effect)
55
+ raise InvalidEffect, "only deny effect can have suggestion" if suggest&& effect != :deny
52
56
  @effect = effect
53
57
  @effect_comment = comment
58
+ @effect_user_facing_message = user_facing_message
59
+ @effect_suggestion = suggest
54
60
  nil
55
61
  end
56
62
 
57
- def allow!(comment = ""); decide!(:allow, comment); end
58
- def continue!(comment = ""); decide!(:continue, comment); end
59
- def deny!(comment = ""); decide!(:deny, comment); end
60
- def skip!(comment = ""); decide!(:skip, comment); end
63
+ def allow!(*args, **kwargs); decide!(:allow, *args, **kwargs); end
64
+ def continue!(*args, **kwargs); decide!(:continue, *args, **kwargs); end
65
+ def deny!(*args, **kwargs); decide!(:deny, *args, **kwargs); end
66
+ def skip!(*args, **kwargs); decide!(:skip, *args, **kwargs); end
61
67
  end
62
68
  end
63
69
  end
@@ -11,11 +11,12 @@ module Himari
11
11
  claims: authz.claims,
12
12
  client_id: authz.client_id,
13
13
  nonce: authz.nonce,
14
+ lifetime: authz.lifetime,
14
15
  **kwargs
15
16
  )
16
17
  end
17
18
 
18
- def initialize(claims:, client_id:, nonce:, signing_key:, issuer:, access_token: nil, time: Time.now)
19
+ def initialize(claims:, client_id:, nonce:, signing_key:, issuer:, access_token: nil, time: Time.now, lifetime: 3600)
19
20
  @claims = claims
20
21
  @client_id = client_id
21
22
  @nonce = nonce
@@ -23,6 +24,7 @@ module Himari
23
24
  @issuer = issuer
24
25
  @access_token = access_token
25
26
  @time = time
27
+ @lifetime = lifetime
26
28
  end
27
29
 
28
30
  attr_reader :claims, :nonce, :signing_key
@@ -34,7 +36,7 @@ module Himari
34
36
  aud: @client_id,
35
37
  iat: @time.to_i,
36
38
  nbf: @time.to_i,
37
- exp: (@time + 3600).to_i, # TODO: lifetime
39
+ exp: (@time + @lifetime).to_i,
38
40
  ).merge(
39
41
  @nonce ? { nonce: @nonce } : {}
40
42
  ).merge(
@@ -2,7 +2,7 @@ module Himari
2
2
  class RuleProcessor
3
3
  class MissingDecisionError < StandardError; end
4
4
 
5
- Result = Struct.new(:rule_name, :allowed, :explicit_deny, :decision, :decision_log, keyword_init: true) do
5
+ Result = Struct.new(:rule_name, :allowed, :explicit_deny, :decision, :decision_log, :user_facing_message, :suggestion, keyword_init: true) do
6
6
  def as_log
7
7
  {
8
8
  rule_name: rule_name,
@@ -10,7 +10,9 @@ module Himari
10
10
  explicit_deny: explicit_deny,
11
11
  decision: decision&.as_log,
12
12
  decision_log: decision_log.map(&:to_h),
13
- }
13
+ }.tap do |x|
14
+ x[:suggestion] = suggestion if suggestion
15
+ end
14
16
  end
15
17
  end
16
18
 
@@ -47,6 +49,7 @@ module Himari
47
49
  result.decision = decision
48
50
  result.allowed = true
49
51
  result.explicit_deny = false
52
+ result.user_facing_message = decision.effect_user_facing_message
50
53
 
51
54
  when :continue
52
55
  @decision = decision
@@ -61,6 +64,8 @@ module Himari
61
64
  result.decision = nil
62
65
  result.allowed = false
63
66
  result.explicit_deny = true
67
+ result.user_facing_message = decision.effect_user_facing_message
68
+ result.suggestion = decision.effect_suggestion
64
69
 
65
70
  else
66
71
  raise "Unknown effect #{decision.effect} [BUG]"
@@ -21,7 +21,7 @@ module Himari
21
21
  end
22
22
  end
23
23
 
24
- Result = Struct.new(:client, :claims, :authz_result) do
24
+ Result = Struct.new(:client, :claims, :lifetime, :authz_result) do
25
25
  def as_log
26
26
  {
27
27
  client: client.as_log,
@@ -63,10 +63,11 @@ module Himari
63
63
  context = Himari::Decisions::Authorization::Context.new(claims: @session.claims, user_data: @session.user_data, request: @request, client: @client).freeze
64
64
 
65
65
  authorization = Himari::RuleProcessor.new(context, Himari::Decisions::Authorization.new(claims: @session.claims.dup)).run(@authz_rules)
66
- raise ForbiddenError.new(Result.new(@client, nil, authorization)) unless authorization.allowed
66
+ raise ForbiddenError.new(Result.new(@client, nil, nil, authorization)) unless authorization.allowed
67
67
 
68
- claims = authorization.decision.output
69
- Result.new(@client, claims, authorization)
68
+ claims = authorization.decision.output_claims
69
+ lifetime = authorization.decision.lifetime
70
+ Result.new(@client, claims, lifetime, authorization)
70
71
  end
71
72
  end
72
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Himari
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -58,6 +58,15 @@ main > header img, main > footer img {
58
58
  margin-top: 30px;
59
59
  }
60
60
 
61
+ .notice {
62
+ background-color: white;
63
+ border: 1px #bfa88a solid;
64
+ border-radius: 4px;
65
+ padding: 4px;
66
+ margin: 12px;
67
+ margin-bottom: 24px;
68
+ }
69
+
61
70
  .d-none {
62
71
  display: none;
63
72
  }
data/views/login.erb CHANGED
@@ -16,6 +16,12 @@
16
16
  <header>
17
17
  <h1><%= msg(:title, "Login to Himari") %></h1>
18
18
  <%= msg(:header) %>
19
+
20
+ <% if @notice %>
21
+ <div class='notice'>
22
+ <p><%=h @notice %></p>
23
+ </div>
24
+ <% end %>
19
25
  </header>
20
26
 
21
27
  <nav class='actions'>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: himari
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sorah Fukumori