brevio-session 0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +56 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +102 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/brevio-session.gemspec +23 -0
- data/lib/brevio/session/config.rb +34 -0
- data/lib/brevio/session/cookies/parse.rb +39 -0
- data/lib/brevio/session/redis.rb +21 -0
- data/lib/brevio/session/testing.rb +62 -0
- data/lib/brevio/session.rb +98 -0
- data/lib/brevio-session.rb +7 -0
- data/lib/brevio_session.rb +16 -0
- data/test/brevio_session_integration_test.rb +38 -0
- data/test/redis_json_wrapper.rb +39 -0
- data/test/stubs.rb +18 -0
- data/test/test_app.rb +36 -0
- data/test/test_helper.rb +36 -0
- metadata +154 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c515c318b1b9d9dcc8f157a9fd507511d2462d43ddb62de9f439ce55e21c5da3
|
|
4
|
+
data.tar.gz: d104172c65410bf4e52d8087a2e8bcb30220cf1ef18d75d7d3f0a5eeaba2cb7d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5cfcdc09b39e5162e49d1ae0f386dfa59a5e5c62a6a80c7e763d5b67fd029bea0b422efba229caf6c525e1e9011f4d84bdf48ada003d92b5918c7a00da80b244
|
|
7
|
+
data.tar.gz: f2deb2e307e1bdcf2f91744bffd969ef5a48dd34638079b22693ca6ba0ac38cdbe342a7ecd525b12b666faff15338ec682add782738693f1ddf0ad0e69288e2e
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.2
|
|
4
|
+
|
|
5
|
+
Layout/EmptyLineAfterGuardClause:
|
|
6
|
+
Enabled: false
|
|
7
|
+
|
|
8
|
+
Layout/FirstHashElementIndentation:
|
|
9
|
+
EnforcedStyle: consistent
|
|
10
|
+
|
|
11
|
+
Layout/HashAlignment:
|
|
12
|
+
EnforcedHashRocketStyle: table
|
|
13
|
+
|
|
14
|
+
Layout/LineLength:
|
|
15
|
+
Max: 100
|
|
16
|
+
|
|
17
|
+
Layout/MultilineMethodCallIndentation:
|
|
18
|
+
EnforcedStyle: indented_relative_to_receiver
|
|
19
|
+
|
|
20
|
+
Metrics/BlockLength:
|
|
21
|
+
Exclude:
|
|
22
|
+
- "*.gemspec"
|
|
23
|
+
|
|
24
|
+
Naming/AccessorMethodName:
|
|
25
|
+
Exclude:
|
|
26
|
+
- test/**/*
|
|
27
|
+
|
|
28
|
+
Metrics/AbcSize:
|
|
29
|
+
Exclude:
|
|
30
|
+
- test/**/stubs/**/*
|
|
31
|
+
|
|
32
|
+
Metrics/ClassLength:
|
|
33
|
+
Exclude:
|
|
34
|
+
- test/**/stubs/**/*
|
|
35
|
+
|
|
36
|
+
Metrics/MethodLength:
|
|
37
|
+
Exclude:
|
|
38
|
+
- test/**/*
|
|
39
|
+
|
|
40
|
+
Style/Alias:
|
|
41
|
+
Exclude:
|
|
42
|
+
- test/**/*
|
|
43
|
+
|
|
44
|
+
Style/ClassAndModuleChildren:
|
|
45
|
+
Enabled: false
|
|
46
|
+
|
|
47
|
+
Style/ClassVars:
|
|
48
|
+
Exclude:
|
|
49
|
+
- test/**/*
|
|
50
|
+
|
|
51
|
+
Style/Documentation:
|
|
52
|
+
Enabled: false
|
|
53
|
+
|
|
54
|
+
Style/ModuleFunction:
|
|
55
|
+
Enabled: true
|
|
56
|
+
EnforcedStyle: extend_self
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.0
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
brevio-session (0.1)
|
|
5
|
+
actionpack (~> 7.0.3)
|
|
6
|
+
redis (~> 5.0)
|
|
7
|
+
zeitwerk (~> 2.6.7)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
actionpack (7.0.4)
|
|
13
|
+
actionview (= 7.0.4)
|
|
14
|
+
activesupport (= 7.0.4)
|
|
15
|
+
rack (~> 2.0, >= 2.2.0)
|
|
16
|
+
rack-test (>= 0.6.3)
|
|
17
|
+
rails-dom-testing (~> 2.0)
|
|
18
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
19
|
+
actionview (7.0.4)
|
|
20
|
+
activesupport (= 7.0.4)
|
|
21
|
+
builder (~> 3.1)
|
|
22
|
+
erubi (~> 1.4)
|
|
23
|
+
rails-dom-testing (~> 2.0)
|
|
24
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
25
|
+
activesupport (7.0.4)
|
|
26
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
27
|
+
i18n (>= 1.6, < 2)
|
|
28
|
+
minitest (>= 5.1)
|
|
29
|
+
tzinfo (~> 2.0)
|
|
30
|
+
ast (2.4.2)
|
|
31
|
+
builder (3.2.4)
|
|
32
|
+
concurrent-ruby (1.1.10)
|
|
33
|
+
connection_pool (2.4.0)
|
|
34
|
+
crass (1.0.6)
|
|
35
|
+
debug (1.5.0)
|
|
36
|
+
irb (>= 1.3.6)
|
|
37
|
+
reline (>= 0.2.7)
|
|
38
|
+
erubi (1.11.0)
|
|
39
|
+
i18n (1.12.0)
|
|
40
|
+
concurrent-ruby (~> 1.0)
|
|
41
|
+
io-console (0.6.0)
|
|
42
|
+
irb (1.6.1)
|
|
43
|
+
reline (>= 0.3.0)
|
|
44
|
+
json (2.6.2)
|
|
45
|
+
loofah (2.19.0)
|
|
46
|
+
crass (~> 1.0.2)
|
|
47
|
+
nokogiri (>= 1.5.9)
|
|
48
|
+
mini_portile2 (2.8.1)
|
|
49
|
+
minitest (5.16.3)
|
|
50
|
+
nokogiri (1.13.9)
|
|
51
|
+
mini_portile2 (~> 2.8.0)
|
|
52
|
+
racc (~> 1.4)
|
|
53
|
+
parallel (1.22.1)
|
|
54
|
+
parser (3.1.2.1)
|
|
55
|
+
ast (~> 2.4.1)
|
|
56
|
+
racc (1.6.1)
|
|
57
|
+
rack (2.2.4)
|
|
58
|
+
rack-test (2.0.2)
|
|
59
|
+
rack (>= 1.3)
|
|
60
|
+
rails-dom-testing (2.0.3)
|
|
61
|
+
activesupport (>= 4.2.0)
|
|
62
|
+
nokogiri (>= 1.6)
|
|
63
|
+
rails-html-sanitizer (1.4.3)
|
|
64
|
+
loofah (~> 2.3)
|
|
65
|
+
rainbow (3.1.1)
|
|
66
|
+
redis (5.0.6)
|
|
67
|
+
redis-client (>= 0.9.0)
|
|
68
|
+
redis-client (0.14.1)
|
|
69
|
+
connection_pool
|
|
70
|
+
regexp_parser (2.5.0)
|
|
71
|
+
reline (0.3.2)
|
|
72
|
+
io-console (~> 0.5)
|
|
73
|
+
rexml (3.2.5)
|
|
74
|
+
rubocop (1.36.0)
|
|
75
|
+
json (~> 2.3)
|
|
76
|
+
parallel (~> 1.10)
|
|
77
|
+
parser (>= 3.1.2.1)
|
|
78
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
79
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
80
|
+
rexml (>= 3.2.5, < 4.0)
|
|
81
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
|
82
|
+
ruby-progressbar (~> 1.7)
|
|
83
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
84
|
+
rubocop-ast (1.21.0)
|
|
85
|
+
parser (>= 3.1.1.0)
|
|
86
|
+
ruby-progressbar (1.11.0)
|
|
87
|
+
tzinfo (2.0.5)
|
|
88
|
+
concurrent-ruby (~> 1.0)
|
|
89
|
+
unicode-display_width (2.2.0)
|
|
90
|
+
zeitwerk (2.6.7)
|
|
91
|
+
|
|
92
|
+
PLATFORMS
|
|
93
|
+
arm64-darwin-21
|
|
94
|
+
|
|
95
|
+
DEPENDENCIES
|
|
96
|
+
brevio-session!
|
|
97
|
+
bundler (~> 2.1)
|
|
98
|
+
debug (~> 1.5.0, <= 1.6.0)
|
|
99
|
+
rubocop (~> 1.35)
|
|
100
|
+
|
|
101
|
+
BUNDLED WITH
|
|
102
|
+
2.3.22
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Nicolay Hvidsten
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Brevio Session
|
|
2
|
+
|
|
3
|
+
This gem is a thin wrapper around the Brevio HTTP session, which is created by the [Brevio ID](https://github.com/team-brevio/id) service, and stored in Redis. The HTTP session is shared by all customer-facing Brevio services, enabling single sign-on.
|
|
4
|
+
|
|
5
|
+
The session is shared by storing the encrypted Redis key in an agreed-upon cookie (`BREVIO_ID_COOKIE`). The passphrase used for the encryption is also agreed-upon between services (`BREVIO_ID_SECRET_KEY`).
|
|
6
|
+
|
|
7
|
+
The gem provides three utility functions, used in the application controllers:
|
|
8
|
+
|
|
9
|
+
- `fetch_brevio_session`: Loads the Brevio session from Redis and returns a `HashWithIndifferentAccess` wrapper around it.
|
|
10
|
+
- `fetch_brevio_session!`: Same as above, but raises an error if the session isn't present.
|
|
11
|
+
- `brevio_logged_in?`: Returns a boolean flag indicating whether there exists a current Brevio session.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
# Gemfile
|
|
17
|
+
gem 'brevio-session'
|
|
18
|
+
|
|
19
|
+
# config/initializers/brevio_session.rb
|
|
20
|
+
Brevio::Session::Config.configure do |config|
|
|
21
|
+
config.debug = ENV.fetch('BREVIO_SESSION_DEBUG', false) # Logs additional information for session retrieval
|
|
22
|
+
config.production = Rails.env.production?
|
|
23
|
+
config.redis = Brevio::Redis::Client.new(ENV.fetch('BREVIO_ID_REDIS_URL'))
|
|
24
|
+
config.secret_key = ENV.fetch('BREVIO_ID_SECRET_KEY')
|
|
25
|
+
config.session_cookie = ENV.fetch('BREVIO_ID_COOKIE')
|
|
26
|
+
config.session_expire = Integer(ENV.fetch('BREVIO_ID_EXPIRE')).minutes
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
To gain access to the utility functions mentioned above, you need to include the `Brevio::Session` module in
|
|
33
|
+
any Rails controller. The module is a [Rails Concern](https://api.rubyonrails.org/v7.0.4/classes/ActiveSupport/Concern.html) which ensures it is being included in the correct context.
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
class ApiController
|
|
37
|
+
include Brevio::Session
|
|
38
|
+
|
|
39
|
+
def action
|
|
40
|
+
brevio_session = fetch_brevio_session
|
|
41
|
+
puts brevio_session
|
|
42
|
+
# => { user_id: 1, audit_company_id: 1, user_stamp: '2022-01-0107:39:58.281894000' }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The session itself contains the following information (subject to change in Brevio ID):
|
|
48
|
+
|
|
49
|
+
- `user_id`: The primary key for the user in the Brevio ID database.
|
|
50
|
+
- `audit_company_id`: The primary key for the audit company in the Brevio ID database.
|
|
51
|
+
- `user_stamp`: Timestamp indicating when the user was last updated in Brevio ID.
|
|
52
|
+
|
|
53
|
+
## Testing
|
|
54
|
+
|
|
55
|
+
Since we want to test features with logged-in users, we need to be able to emulate a shared Brevio session.
|
|
56
|
+
|
|
57
|
+
First off, we need to mock the Redis session, which is done by calling the `Brevio::Session::Testing.setup!` function.
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
Brevio::Session::Testing.setup!(logger: Rails.logger)
|
|
61
|
+
# => '--- 👨🔬 Setting up Brevio Session gem for testing 👨🔬 ---'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
We can then simulate a logged-in user by calling the `Brevio::Session::Testing.brevio_login` function.
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
class TestCase < ActionDispatch::IntegrationTest
|
|
68
|
+
include Brevio::Session::Testing
|
|
69
|
+
|
|
70
|
+
let(:user) { create(:user) }
|
|
71
|
+
|
|
72
|
+
test 'is logged in' do
|
|
73
|
+
brevio_login(user)
|
|
74
|
+
get(dashboard_path(user))
|
|
75
|
+
assert_response(:ok)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = 'brevio-session'
|
|
5
|
+
spec.version = '0.1'
|
|
6
|
+
spec.authors = ['Brevio AS']
|
|
7
|
+
spec.email = ['support@brevio.com']
|
|
8
|
+
spec.files = `git ls-files -z`.split("\0")
|
|
9
|
+
|
|
10
|
+
spec.homepage = 'https://github.com/team-brevio/brevio-session'
|
|
11
|
+
spec.summary = 'Brevio session wrapper'
|
|
12
|
+
spec.required_ruby_version = '3.2'
|
|
13
|
+
spec.description = 'Wrapper around HTTP session set by Brevio ID'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
spec.add_development_dependency 'bundler', '~> 2.1'
|
|
17
|
+
spec.add_development_dependency 'debug', '~> 1.5.0', '<= 1.6.0'
|
|
18
|
+
spec.add_development_dependency 'rubocop', '~> 1.35'
|
|
19
|
+
spec.add_dependency 'actionpack', '~> 7.0.3'
|
|
20
|
+
spec.add_dependency 'redis', '~> 5.0'
|
|
21
|
+
spec.add_dependency 'zeitwerk', '~> 2.6.7'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Brevio::Session::Config
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
attr_accessor :config
|
|
7
|
+
|
|
8
|
+
def configure
|
|
9
|
+
self.config ||= Configuration.new
|
|
10
|
+
yield(config)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Configuration
|
|
14
|
+
attr_accessor(
|
|
15
|
+
:debug,
|
|
16
|
+
:encryption_key,
|
|
17
|
+
:logger,
|
|
18
|
+
:production,
|
|
19
|
+
:redis,
|
|
20
|
+
:session_cookie,
|
|
21
|
+
:session_expire
|
|
22
|
+
)
|
|
23
|
+
alias debug? debug
|
|
24
|
+
alias production? production
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module Redis
|
|
28
|
+
module Prefixes
|
|
29
|
+
AUDIT_COMPANY = 'brevio-id:audit-company'
|
|
30
|
+
SESSION = 'brevio-id:session'
|
|
31
|
+
USER = 'brevio-id:user'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Brevio::Session
|
|
4
|
+
module Cookies::Parse
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def perform!(cookie)
|
|
8
|
+
raise NilSession if cookie.nil?
|
|
9
|
+
data, iv, auth_tag = cookie.split('--').map { |value| Base64.decode64(value) }
|
|
10
|
+
cipher = OpenSSL::Cipher.new(CIPHER)
|
|
11
|
+
secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(brevio_config.encryption_key,
|
|
12
|
+
SALT, 1000, cipher.key_len)
|
|
13
|
+
|
|
14
|
+
cipher.decrypt
|
|
15
|
+
cipher.key = secret
|
|
16
|
+
cipher.iv = iv
|
|
17
|
+
cipher.auth_tag = auth_tag
|
|
18
|
+
cipher.auth_data = ''
|
|
19
|
+
|
|
20
|
+
cookie_payload = cipher.update(data)
|
|
21
|
+
cookie_payload << cipher.final
|
|
22
|
+
cookie_payload = JSON.parse(cookie_payload)
|
|
23
|
+
key = JSON.parse(Base64.decode64(cookie_payload['_rails']['message']))
|
|
24
|
+
|
|
25
|
+
"#{Config::Redis::Prefixes::SESSION}:#{key}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# https://github.com/team-brevio/brevio-id-gem/blob/master/lib/brevio_id/session/cookie_jar.rb#L79
|
|
31
|
+
CIPHER = 'aes-256-gcm'
|
|
32
|
+
# https://github.com/team-brevio/brevio-id-gem/blob/master/lib/brevio_id/session/cookie_jar.rb#L84
|
|
33
|
+
SALT = 'authenticated encrypted cookie'
|
|
34
|
+
|
|
35
|
+
def brevio_config
|
|
36
|
+
Config.config
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Brevio::Session
|
|
4
|
+
module Redis
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def get(key)
|
|
8
|
+
brevio_config.redis.get(key)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def expire(key, exp)
|
|
12
|
+
brevio_config.redis.expire(key, exp)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def brevio_config
|
|
18
|
+
Config.config
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Brevio::Session
|
|
4
|
+
# Module used to enable testing for controllers using the Brevio session.
|
|
5
|
+
# Mocks a login to a shared redis, with keys generated by the Brevio::Session::CookieJar.
|
|
6
|
+
#
|
|
7
|
+
module Testing
|
|
8
|
+
# Simulates a user with a shared Brevio session. We can specify which *brevio_id* the
|
|
9
|
+
# mocked ID service should return (as well as a custom last updated_at timestamp).
|
|
10
|
+
#
|
|
11
|
+
# The user needs to have the following methods defined: #brevio_id, #audit_company
|
|
12
|
+
#
|
|
13
|
+
def brevio_login(user, updated_at: Time.current.yesterday)
|
|
14
|
+
redis_key = SecureRandom.hex(6)
|
|
15
|
+
|
|
16
|
+
session_hash = { user_id: user.brevio_id,
|
|
17
|
+
audit_company_id: user.audit_company.brevio_id,
|
|
18
|
+
user_stamp: updated_at }
|
|
19
|
+
|
|
20
|
+
Testing.config.logger.info "setting Brevio session to #{session_hash}"
|
|
21
|
+
|
|
22
|
+
Testing.config.gem_config.redis.set(
|
|
23
|
+
redis_key,
|
|
24
|
+
session_hash
|
|
25
|
+
)
|
|
26
|
+
set_cookie(redis_key)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def brevio_logout
|
|
30
|
+
cookies[Testing.config.gem_config.session_cookie] = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# rubocop:disable Naming/AccessorMethodName (set makes sense here)
|
|
34
|
+
def set_cookie(value)
|
|
35
|
+
cookies[Testing.config.gem_config.session_cookie] = value
|
|
36
|
+
end
|
|
37
|
+
# rubocop:enable Naming/AccessorMethodName
|
|
38
|
+
|
|
39
|
+
extend self
|
|
40
|
+
|
|
41
|
+
attr_accessor :sessions, :config
|
|
42
|
+
|
|
43
|
+
def setup!(logger:)
|
|
44
|
+
self.config = Config.new
|
|
45
|
+
config.logger = logger
|
|
46
|
+
config.gem_config = Brevio::Session::Config.config
|
|
47
|
+
config.logger.info '--- 👨🔬 Setting up Brevio Session gem for testing 👨🔬 ---'
|
|
48
|
+
|
|
49
|
+
# Ensures we return a mocked value of the session, rather than something which depends on
|
|
50
|
+
# the cryptographically signed value.
|
|
51
|
+
#
|
|
52
|
+
Brevio::Session::Cookies::Parse.send(:define_method, :perform!) do |cookie|
|
|
53
|
+
raise NilSession if cookie.nil?
|
|
54
|
+
cookie
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class Config
|
|
59
|
+
attr_accessor :logger, :gem_config
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/concern'
|
|
4
|
+
|
|
5
|
+
module Brevio::Session
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
# By including this module into a Rails controller you will have access to the convenience methods
|
|
9
|
+
# fetch_brevio_session(!) and brevio_logged_in? which acts as a wrapper around the decrypting of
|
|
10
|
+
# predefined session cookies and sessions stored in Redis.
|
|
11
|
+
#
|
|
12
|
+
# We also require access to instance attributes like #request and the cookie jar, which are only
|
|
13
|
+
# available in the context of a controller.
|
|
14
|
+
#
|
|
15
|
+
included do
|
|
16
|
+
raise 'Included Brevio::Session outside of controller' unless self <= ActionController::Base
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Fetches the Brevio session from Redis, based on the encrypted key stored in the client's cookie.
|
|
20
|
+
# Returns *nil* if there's no session present.
|
|
21
|
+
#
|
|
22
|
+
def fetch_brevio_session
|
|
23
|
+
brevio_session, redis_key = fetch_session
|
|
24
|
+
return nil if brevio_session.nil?
|
|
25
|
+
if brevio_config.debug?
|
|
26
|
+
brevio_config.logger.info "[brevio-session] Found session #{brevio_session.inspect}"
|
|
27
|
+
end
|
|
28
|
+
refresh_session(redis_key) unless params.transform_keys(&:underscore)[:no_session].present?
|
|
29
|
+
brevio_session
|
|
30
|
+
rescue RuntimeError => e
|
|
31
|
+
brevio_config.logger.error "[brevio-session] #{e.message}"
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Calls the above function, but raises an exception if the session isn't present.
|
|
36
|
+
def fetch_brevio_session!
|
|
37
|
+
brevio_session, redis_key = fetch_session
|
|
38
|
+
raise NilSession, 'Brevio session was nil' if brevio_session.nil?
|
|
39
|
+
refresh_session(redis_key) unless params.transform_keys(&:underscore)[:no_session].present?
|
|
40
|
+
brevio_session
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns a boolean flag indicating whether the current client has a Brevio session cookie set,
|
|
44
|
+
# and whether this cookie contains a user ID.
|
|
45
|
+
#
|
|
46
|
+
def brevio_logged_in?
|
|
47
|
+
fetch_session&.dig(:user_id).present?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def brevio_config
|
|
53
|
+
Config.config
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def fetch_session
|
|
57
|
+
brevio_config.logger.info '[brevio-session] Fetching Brevio session'
|
|
58
|
+
cookie = request.cookie_jar[brevio_config.session_cookie]
|
|
59
|
+
redis_key = Cookies::Parse.perform!(cookie)
|
|
60
|
+
brevio_session = Redis.get(redis_key)
|
|
61
|
+
raise NilSession if brevio_session.nil?
|
|
62
|
+
[brevio_session.with_indifferent_access, redis_key]
|
|
63
|
+
rescue RuntimeError => e
|
|
64
|
+
brevio_config.logger.error "[brevio-session] --- 💣 Couldn't fetch Brevio session 💣 ---"
|
|
65
|
+
brevio_config.logger.error "[brevio-session] #{e.message}"
|
|
66
|
+
nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Refreshes the Brevio session cookie, avoding its expiry. This is helpful to
|
|
70
|
+
# ensure the user stays logged in through interacting with the application.
|
|
71
|
+
#
|
|
72
|
+
def refresh_session(redis_key)
|
|
73
|
+
brevio_config.logger.info '[brevio-session] Refreshing Brevio session' if brevio_config.debug?
|
|
74
|
+
cookies[brevio_config.session_cookie] = {
|
|
75
|
+
value: request.cookie_jar[brevio_config.session_cookie],
|
|
76
|
+
domain: :all,
|
|
77
|
+
expires: brevio_config.session_expire,
|
|
78
|
+
httponly: true,
|
|
79
|
+
secure: true
|
|
80
|
+
}
|
|
81
|
+
Redis.expire(redis_key, brevio_config.session_expire)
|
|
82
|
+
rescue RuntimeError => e
|
|
83
|
+
brevio_config.logger.error "[brevio-session] --- 💣 Couldn't refresh Brevio session 💣 ---"
|
|
84
|
+
brevio_config.logger.error "[brevio-session] #{e.message}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class << self
|
|
88
|
+
# Function used to fetch the current audit company session for a given Brevio ID.
|
|
89
|
+
# This session contains information about an audit company shared by any number of
|
|
90
|
+
# users. If the audit company has been updated in ID (e.g. changed logo or name), the
|
|
91
|
+
# database record of the local service (e.g. Confirm/Sign) will fetch the updates if
|
|
92
|
+
# the timestamp in this session is greater than their own local timestamps.
|
|
93
|
+
#
|
|
94
|
+
def audit_company_updated_at(brevio_id)
|
|
95
|
+
Config.config.redis.get("#{Config::Redis::Prefixes::AUDIT_COMPANY}:#{brevio_id}")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'zeitwerk'
|
|
4
|
+
|
|
5
|
+
# Silences warnings for auto-defined constants, e.g. "Brevio" in this case due to having a "Brevio" folder
|
|
6
|
+
Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
|
|
7
|
+
|
|
8
|
+
loader = Zeitwerk::Loader.for_gem
|
|
9
|
+
loader.ignore("#{__dir__}/brevio-session.rb")
|
|
10
|
+
loader.setup # ready!
|
|
11
|
+
|
|
12
|
+
require 'action_dispatch'
|
|
13
|
+
|
|
14
|
+
module Brevio::Session
|
|
15
|
+
class NilSession < StandardError; end
|
|
16
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class BrevioSessionIntegrationTest < Minitest::Test
|
|
6
|
+
include Rack::Test::Methods
|
|
7
|
+
include Brevio::Session::Testing
|
|
8
|
+
|
|
9
|
+
def brevio_redis
|
|
10
|
+
Brevio::Session::Config.config.redis
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def user
|
|
14
|
+
UserStub.new(brevio_id: 1)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_sets_brevio_session_in_redis
|
|
18
|
+
session = brevio_login(user)
|
|
19
|
+
get '/', nil, cookie: "brevio_session=#{session}"
|
|
20
|
+
assert last_response.ok?
|
|
21
|
+
assert current_session.cookie_jar['brevio_session']
|
|
22
|
+
assert_equal user.brevio_id, brevio_redis.get("brevio-id:session:#{session}")[:user_id]
|
|
23
|
+
json_body = JSON.parse(last_response.body)
|
|
24
|
+
assert_equal user.brevio_id, json_body['user_id']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_user_stamp
|
|
28
|
+
time = Time.current
|
|
29
|
+
session = brevio_login(user, updated_at: time)
|
|
30
|
+
get '/', nil, cookie: "brevio_session=#{session}"
|
|
31
|
+
assert last_response.ok?
|
|
32
|
+
json_body = JSON.parse(last_response.body)
|
|
33
|
+
# ceil ceils the subsecond to a default decimal (0) by default. We don't really care about
|
|
34
|
+
# microsecond precision here during the JSON parsing/dumping
|
|
35
|
+
assert_equal time.ceil, Time.parse(json_body['user_stamp']).ceil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Brevio ID stores its session using redis-session-store, which stores all values as JSON.
|
|
4
|
+
# The returned value is a JSON string, which needs to be parsed by the redis client. The native
|
|
5
|
+
# Redis.get would simply return the JSON string, so we need to wrap it.
|
|
6
|
+
#
|
|
7
|
+
# The services consuming the Brevio::Session (e.g. Confirm/Sign) already assume the values
|
|
8
|
+
# in Redis are all JSON (see domain/brevio/redis/client.rb).
|
|
9
|
+
#
|
|
10
|
+
#
|
|
11
|
+
class RedisJSONWrapper
|
|
12
|
+
def set(key, value)
|
|
13
|
+
redis.set(key, value.to_json)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get(key)
|
|
17
|
+
raw = redis.get(key)
|
|
18
|
+
return nil if raw.nil?
|
|
19
|
+
|
|
20
|
+
value = JSON.parse(raw)
|
|
21
|
+
return value if value.empty?
|
|
22
|
+
|
|
23
|
+
value.with_indifferent_access
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def del(keys)
|
|
27
|
+
redis.del(keys)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def expire(key, expiry)
|
|
31
|
+
redis.expire(key, expiry)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def redis
|
|
37
|
+
@redis ||= ::Redis.new
|
|
38
|
+
end
|
|
39
|
+
end
|
data/test/stubs.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UserStub
|
|
4
|
+
attr_accessor :brevio_id, :audit_company
|
|
5
|
+
|
|
6
|
+
def initialize(attrs)
|
|
7
|
+
@brevio_id = attrs[:brevio_id]
|
|
8
|
+
@audit_company = AuditCompanyStub.new(brevio_id: @brevio_id + 1)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class AuditCompanyStub
|
|
13
|
+
attr_accessor :brevio_id
|
|
14
|
+
|
|
15
|
+
def initialize(attrs)
|
|
16
|
+
@brevio_id = attrs[:brevio_id]
|
|
17
|
+
end
|
|
18
|
+
end
|
data/test/test_app.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Based on https://github.com/rails/rails/blob/6-0-stable/guides/bug_report_templates/action_controller_master.rb
|
|
4
|
+
|
|
5
|
+
require 'action_controller/railtie'
|
|
6
|
+
require 'rack/test'
|
|
7
|
+
require 'brevio-session'
|
|
8
|
+
|
|
9
|
+
class TestApp < Rails::Application
|
|
10
|
+
config.root = __dir__
|
|
11
|
+
config.hosts << 'example.org'
|
|
12
|
+
config.session_store :cookie_store, key: 'brevio_session'
|
|
13
|
+
secrets.secret_key_base = 'secret_key_base'
|
|
14
|
+
|
|
15
|
+
config.logger = Logger.new($stdout)
|
|
16
|
+
config.log_level = :info
|
|
17
|
+
Rails.logger = config.logger
|
|
18
|
+
|
|
19
|
+
routes.draw do
|
|
20
|
+
get '/' => 'test#index'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class TestController < ActionController::Base
|
|
25
|
+
include Brevio::Session
|
|
26
|
+
include Rails.application.routes.url_helpers
|
|
27
|
+
|
|
28
|
+
def logger
|
|
29
|
+
Rails.logger
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def index
|
|
33
|
+
brevio_session = fetch_brevio_session
|
|
34
|
+
render json: brevio_session.to_json
|
|
35
|
+
end
|
|
36
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'minitest/autorun'
|
|
4
|
+
require 'rack/test'
|
|
5
|
+
|
|
6
|
+
require 'debug'
|
|
7
|
+
require 'fakeredis'
|
|
8
|
+
require 'priscilla'
|
|
9
|
+
require 'brevio-session'
|
|
10
|
+
require 'brevio_session/testing'
|
|
11
|
+
|
|
12
|
+
require 'redis_json_wrapper'
|
|
13
|
+
require 'stubs'
|
|
14
|
+
require 'test_app'
|
|
15
|
+
|
|
16
|
+
Brevio::Session::Config.configure do |config|
|
|
17
|
+
config.debug = true
|
|
18
|
+
config.production = false
|
|
19
|
+
config.redis = RedisJSONWrapper.new
|
|
20
|
+
config.secret_key = 'secret_key_base'
|
|
21
|
+
config.session_cookie = 'brevio_session'
|
|
22
|
+
config.session_expire = 45.minutes
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Brevio::Session::Testing.setup!(logger: Rails.logger)
|
|
26
|
+
|
|
27
|
+
module Minitest
|
|
28
|
+
class Test
|
|
29
|
+
include Rack::Test::Methods
|
|
30
|
+
include TestApp.routes.url_helpers
|
|
31
|
+
|
|
32
|
+
def app
|
|
33
|
+
TestApp
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: brevio-session
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '0.1'
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Brevio AS
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-04-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.1'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: debug
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 1.5.0
|
|
34
|
+
- - "<="
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: 1.6.0
|
|
37
|
+
type: :development
|
|
38
|
+
prerelease: false
|
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - "~>"
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 1.5.0
|
|
44
|
+
- - "<="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 1.6.0
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: rubocop
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.35'
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.35'
|
|
61
|
+
- !ruby/object:Gem::Dependency
|
|
62
|
+
name: actionpack
|
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 7.0.3
|
|
68
|
+
type: :runtime
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 7.0.3
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: redis
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '5.0'
|
|
82
|
+
type: :runtime
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '5.0'
|
|
89
|
+
- !ruby/object:Gem::Dependency
|
|
90
|
+
name: zeitwerk
|
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 2.6.7
|
|
96
|
+
type: :runtime
|
|
97
|
+
prerelease: false
|
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 2.6.7
|
|
103
|
+
description: Wrapper around HTTP session set by Brevio ID
|
|
104
|
+
email:
|
|
105
|
+
- support@brevio.com
|
|
106
|
+
executables: []
|
|
107
|
+
extensions: []
|
|
108
|
+
extra_rdoc_files: []
|
|
109
|
+
files:
|
|
110
|
+
- ".gitignore"
|
|
111
|
+
- ".rubocop.yml"
|
|
112
|
+
- ".ruby-version"
|
|
113
|
+
- Gemfile
|
|
114
|
+
- Gemfile.lock
|
|
115
|
+
- LICENSE.txt
|
|
116
|
+
- README.md
|
|
117
|
+
- brevio-session.gemspec
|
|
118
|
+
- lib/brevio-session.rb
|
|
119
|
+
- lib/brevio/session.rb
|
|
120
|
+
- lib/brevio/session/config.rb
|
|
121
|
+
- lib/brevio/session/cookies/parse.rb
|
|
122
|
+
- lib/brevio/session/redis.rb
|
|
123
|
+
- lib/brevio/session/testing.rb
|
|
124
|
+
- lib/brevio_session.rb
|
|
125
|
+
- test/brevio_session_integration_test.rb
|
|
126
|
+
- test/redis_json_wrapper.rb
|
|
127
|
+
- test/stubs.rb
|
|
128
|
+
- test/test_app.rb
|
|
129
|
+
- test/test_helper.rb
|
|
130
|
+
homepage: https://github.com/team-brevio/brevio-session
|
|
131
|
+
licenses:
|
|
132
|
+
- MIT
|
|
133
|
+
metadata:
|
|
134
|
+
rubygems_mfa_required: 'true'
|
|
135
|
+
post_install_message:
|
|
136
|
+
rdoc_options: []
|
|
137
|
+
require_paths:
|
|
138
|
+
- lib
|
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - '='
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '3.2'
|
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
|
+
requirements:
|
|
146
|
+
- - ">="
|
|
147
|
+
- !ruby/object:Gem::Version
|
|
148
|
+
version: '0'
|
|
149
|
+
requirements: []
|
|
150
|
+
rubygems_version: 3.4.1
|
|
151
|
+
signing_key:
|
|
152
|
+
specification_version: 4
|
|
153
|
+
summary: Brevio session wrapper
|
|
154
|
+
test_files: []
|