govuk_personalisation 0.4.0 → 0.8.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: 267d1e8727e692cb953532c2064e3730455845c95a78caa468e82525b5800864
4
- data.tar.gz: 1d303703827774ca360100948fbd3a2a98c7145ae02153b1faaa801107a961f3
3
+ metadata.gz: 03aab9f90629204c9ebb98b0eb31c739206533b5e63b91e2a2181bc5b2b1517f
4
+ data.tar.gz: 7720c391869279bd8841e24ce815b85d8ce52e251e9bb1f6ce15e525b06b9922
5
5
  SHA512:
6
- metadata.gz: 29720a981741b63b503fd28affdf48b41f9f2ab0e7f28368db85d9d4e81726a3264e771dc1b4e5696d4190470cbe2450f949735795f88e2c9182dbc35a370f9d
7
- data.tar.gz: ff365c527b710f736e34470364b3679c37328734f80ebfc5d09e25a50c833e410e3cd2472f90a86ebc35cf456c34632128f6b590beb43bc425a37de82de56682
6
+ metadata.gz: a3619a1c907c386f134dd0e8153851a123e27783810d22885bd969a20b33b7faeed88be4735c1a84673053a7c7c99ba05cb422af42b8952157ce1538e2b02c95
7
+ data.tar.gz: 3fbfd164e7f62849cb8f14dc735734ade9f44dbb4621a160f374544fd63c36c4519313e9989c972a318d49aab04f5a2533e568b035183119c19507e0adcdcdfc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # 0.8.0
2
+
3
+ - Change sign in path to `/sign-in/redirect` ([#17](https://github.com/alphagov/govuk_personalisation/pull/17))
4
+
5
+ # 0.7.0
6
+
7
+ - Add `GovukPersonalisation::Urls` module ([#14](https://github.com/alphagov/govuk_personalisation/pull/14) [#16](https://github.com/alphagov/govuk_personalisation/pull/16))
8
+
9
+ # 0.6.0
10
+
11
+ - Add `GovukPersonalisation::Flash` and helper methods to the concern ([#9](https://github.com/alphagov/govuk_personalisation/pull/9))
12
+ - Ensure every method has RDoc ([#10](https://github.com/alphagov/govuk_personalisation/pull/10))
13
+ - Remove unused `GovukPersonalisation::Error` class ([#10](https://github.com/alphagov/govuk_personalisation/pull/10))
14
+ - BREAKING: Rename `GovukPersonalisation::AccountConcern` to `GovukPersonalisation::ControllerConcern` ([#11](https://github.com/alphagov/govuk_personalisation/pull/11))
15
+
16
+ # 0.5.0
17
+
18
+ - Rename header name constants ([#7](https://github.com/alphagov/govuk_personalisation/pull/7))
19
+
1
20
  # 0.4.0
2
21
 
3
22
  - Add ability to set GOVUK-Account-Session ([#6](https://github.com/alphagov/govuk_personalisation/pull/6))
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # GovukPersonalisation
1
+ # GOV.UK Personalisation
2
2
 
3
- A gem to hold shared code which other GOV.UK apps will use to implement
3
+ A gem to hold shared code which other GOV.UK apps use to implement
4
4
  accounts-related functionality.
5
5
 
6
6
  ## Installation
@@ -17,13 +17,64 @@ And then execute:
17
17
  $ bundle install
18
18
  ```
19
19
 
20
- ## Technical documentation
20
+ ## Usage
21
21
 
22
- <!-- TODO -->...
22
+ ### Rails concern
23
23
 
24
- ### Testing
24
+ Include the concern into a controller:
25
25
 
26
- <!-- TODO -->...
26
+ ```ruby
27
+ include GovukPersonalisation::AccountConcern
28
+ ```
29
+
30
+ And it will add `before_action` methods to:
31
+
32
+ - fetch the account session identifier from the request headers, making it available as `account_session_header`
33
+ - set a `Vary` response header, to ensure responses for different users are cached differently by the CDN
34
+
35
+ The following functions are available:
36
+
37
+ - `logged_in?` - check if there is an account session header
38
+ - `set_account_session_header` - replace the current session value, and set the response header the CDN uses to update the user's cookie
39
+ - `logout!` - clear the session value, and set the response header the CDN uses to remove the user's cookie
40
+
41
+ When run in development mode (`RAILS_ENV=development`), a cookie on
42
+ `dev.gov.uk` is used instead of custom headers.
43
+
44
+ ### Test helpers
45
+
46
+ There are test helpers for both request and feature specs to log the
47
+ user in. Include the relevant helper:
48
+
49
+ ```ruby
50
+ include GovukPersonalisation::TestHelpers::Requests
51
+ ```
52
+
53
+ *or*
54
+
55
+ ```ruby
56
+ include GovukPersonalisation::TestHelpers::Features
57
+ ```
58
+
59
+ And then log the user in:
60
+
61
+ ```ruby
62
+ before do
63
+ mock_logged_in_session
64
+ end
65
+ ```
66
+
67
+ If you need a specific session identifier, for example to match
68
+ against it in WebMock stubs or with the [gds-api-adapters][] test
69
+ helpers, you can pass it as an argument:
70
+
71
+ ```ruby
72
+ before do
73
+ mock_logged_in_session("your-session-identifier-goes-here")
74
+ end
75
+ ```
76
+
77
+ [gds-api-adapters]: https://github.com/alphagov/gds-api-adapters
27
78
 
28
79
  ## License
29
80
 
@@ -25,9 +25,11 @@ Gem::Specification.new do |spec|
25
25
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = %w[lib]
27
27
 
28
+ spec.add_dependency "plek", ">= 1.9.0"
28
29
  spec.add_dependency "rails", "~> 6"
29
30
 
30
31
  spec.add_development_dependency "bundler"
32
+ spec.add_development_dependency "climate_control"
31
33
  spec.add_development_dependency "pry-byebug"
32
34
  spec.add_development_dependency "rake"
33
35
  spec.add_development_dependency "rspec-rails"
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module GovukPersonalisation
6
+ module ControllerConcern
7
+ extend ActiveSupport::Concern
8
+
9
+ ACCOUNT_SESSION_INTERNAL_HEADER_NAME = "HTTP_GOVUK_ACCOUNT_SESSION"
10
+ ACCOUNT_SESSION_HEADER_NAME = "GOVUK-Account-Session"
11
+ ACCOUNT_END_SESSION_HEADER_NAME = "GOVUK-Account-End-Session"
12
+ ACCOUNT_SESSION_DEV_COOKIE_NAME = "govuk_account_session"
13
+
14
+ included do
15
+ before_action :fetch_account_session_header
16
+ before_action :set_account_vary_header
17
+ attr_accessor :account_session_header
18
+ attr_reader :account_flash
19
+ end
20
+
21
+ # Read the `GOVUK-Account-Session` request header and set the
22
+ # `@account_session_header` and `@account_flash` variables. Also
23
+ # sets a response header with an empty flash if there is a flash
24
+ # in the request.
25
+ #
26
+ # This is called as a `before_action`
27
+ #
28
+ # This should not be called after either of the
29
+ # `@govuk_account_session` or flash to return to the user have
30
+ # been changed, as those changes will be overwritten.
31
+ def fetch_account_session_header
32
+ session_with_flash =
33
+ if request.headers[ACCOUNT_SESSION_INTERNAL_HEADER_NAME]
34
+ request.headers[ACCOUNT_SESSION_INTERNAL_HEADER_NAME].presence
35
+ elsif Rails.env.development?
36
+ cookies[ACCOUNT_SESSION_DEV_COOKIE_NAME]
37
+ end
38
+
39
+ @account_session_header, flash = GovukPersonalisation::Flash.decode_session(session_with_flash)
40
+ @account_flash = (flash || []).index_with { |_| true }
41
+ @new_account_flash = {}
42
+
43
+ set_account_session_header unless @account_flash.empty?
44
+ end
45
+
46
+ # Set the `Vary: GOVUK-Account-Session` response header.
47
+ #
48
+ # This is called as a `before_action`, to ensure that pages
49
+ # rendered using one user's session are not served to another by
50
+ # our CDN. You should only skip this action if you are certain
51
+ # that the response does not include any personalisation, or if
52
+ # you prevent caching in some other way (for example, with
53
+ # `Cache-Control: no-store`).
54
+ def set_account_vary_header
55
+ response.headers["Vary"] = [response.headers["Vary"], ACCOUNT_SESSION_HEADER_NAME].compact.join(", ")
56
+ end
57
+
58
+ # Check if the user has a session.
59
+ #
60
+ # This does not call account-api to verify that the session is
61
+ # valid, but an invalid session would not allow a user to access
62
+ # any personal data anyway.
63
+ #
64
+ # @return [true, false] whether the user has a session
65
+ def logged_in?
66
+ account_session_header.present?
67
+ end
68
+
69
+ # Set a new session header.
70
+ #
71
+ # This should be called after any API call to account-api which
72
+ # returns a new session value. This is called automatically after
73
+ # updating the flash with `account_flash_add` or
74
+ # `account_flash_keep`
75
+ #
76
+ # Calling this after calling `logout!` will not prevent the user
77
+ # from being logged out.
78
+ #
79
+ # @param govuk_account_session [String, nil] the new session identifier
80
+ def set_account_session_header(govuk_account_session = nil)
81
+ @account_session_header = govuk_account_session if govuk_account_session
82
+
83
+ session_with_flash = GovukPersonalisation::Flash.encode_session(@account_session_header, @new_account_flash.keys)
84
+
85
+ response.headers[ACCOUNT_SESSION_HEADER_NAME] = session_with_flash
86
+ if Rails.env.development?
87
+ cookies[ACCOUNT_SESSION_DEV_COOKIE_NAME] = {
88
+ value: session_with_flash,
89
+ domain: "dev.gov.uk",
90
+ }
91
+ end
92
+ end
93
+
94
+ # Clear the `@account_session_header` and set the logout response
95
+ # header.
96
+ def logout!
97
+ response.headers[ACCOUNT_END_SESSION_HEADER_NAME] = "1"
98
+ @account_session_header = nil
99
+ if Rails.env.development?
100
+ cookies[ACCOUNT_SESSION_DEV_COOKIE_NAME] = {
101
+ value: "",
102
+ domain: "dev.gov.uk",
103
+ expires: 1.second.ago,
104
+ }
105
+ end
106
+ end
107
+
108
+ # Add a message to the flash to return to the user. This does not
109
+ # change `account_flash`
110
+ #
111
+ # @param message [String] the message to add
112
+ #
113
+ # @return [true, false] whether the message is valid (and so has been added)
114
+ def account_flash_add(message)
115
+ return false unless GovukPersonalisation::Flash.valid_message? message
116
+
117
+ @new_account_flash[message] = true
118
+ set_account_session_header
119
+ true
120
+ end
121
+
122
+ # Copy all messages from the `account_flash` into the flash to
123
+ # return to the user.
124
+ def account_flash_keep
125
+ @new_account_flash = @account_flash.merge(@new_account_flash)
126
+ set_account_session_header
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string-literal: true
2
+
3
+ module GovukPersonalisation::Flash
4
+ SESSION_SEPARATOR = "$$"
5
+ MESSAGE_SEPARATOR = ","
6
+ MESSAGE_REGEX = /\A[a-zA-Z0-9._\-]+\z/.freeze
7
+
8
+ # Splits the session header into a session value (suitable for using
9
+ # in account-api calls) and flash messages.
10
+ #
11
+ # @param encoded_session [String] the value of the `GOVUK-Account-Session` header
12
+ #
13
+ # @return [Array(String, Array<String>), nil] the session value and the flash messages
14
+ def self.decode_session(encoded_session)
15
+ session_bits = encoded_session&.split(SESSION_SEPARATOR)
16
+ return if session_bits.blank?
17
+
18
+ if session_bits.length == 1
19
+ [session_bits[0], []]
20
+ else
21
+ [session_bits[0], session_bits[1].split(MESSAGE_SEPARATOR)]
22
+ end
23
+ end
24
+
25
+ # Encodes the session value and a list of flash messages into a
26
+ # session header which can be returned to the user.
27
+ #
28
+ # @param session [String] the session value
29
+ # @param flash [Array<String>] the flash messages, which must all be `valid_message?`
30
+ #
31
+ # @return [String] the encoded session header value
32
+ def self.encode_session(session, flash)
33
+ if flash.blank?
34
+ session
35
+ else
36
+ "#{session}#{SESSION_SEPARATOR}#{flash.join(MESSAGE_SEPARATOR)}"
37
+ end
38
+ end
39
+
40
+ # Check if a string is valid as a flash message.
41
+ #
42
+ # @param message [String, nil] the flash message
43
+ #
44
+ # @return [true, false] whether the message is valid or not.
45
+ def self.valid_message?(message)
46
+ return false if message.nil?
47
+
48
+ message.match? MESSAGE_REGEX
49
+ end
50
+ end
@@ -1,8 +1,13 @@
1
1
  module GovukPersonalisation
2
2
  module TestHelpers
3
3
  module Features
4
- def mock_logged_in_session(value = "placeholder")
5
- page.driver.header("GOVUK-Account-Session", value)
4
+ # Set the `GOVUK-Account-Session` request header in the page
5
+ # driver.
6
+ #
7
+ # @param session_id [String] the session identifier
8
+ # @param flash [Array<String>, nil] the flash messages
9
+ def mock_logged_in_session(session_id = "placeholder", flash = nil)
10
+ page.driver.header("GOVUK-Account-Session", GovukPersonalisation::Flash.encode_session(session_id, flash))
6
11
  end
7
12
  end
8
13
  end
@@ -1,8 +1,12 @@
1
1
  module GovukPersonalisation
2
2
  module TestHelpers
3
3
  module Requests
4
- def mock_logged_in_session(value = "placeholder")
5
- request.headers["GOVUK-Account-Session"] = value
4
+ # Set the `GOVUK-Account-Session` request header.
5
+ #
6
+ # @param session_id [String] the session identifier
7
+ # @param flash [Array<String>, nil] the flash messages
8
+ def mock_logged_in_session(session_id = "placeholder", flash = nil)
9
+ request.headers["GOVUK-Account-Session"] = GovukPersonalisation::Flash.encode_session(session_id, flash)
6
10
  end
7
11
  end
8
12
  end
@@ -0,0 +1,87 @@
1
+ require "plek"
2
+
3
+ module GovukPersonalisation::Urls
4
+ # Find the GOV.UK URL for the "sign in" page
5
+ #
6
+ # @return [String] the URL
7
+ def self.sign_in
8
+ find_govuk_url(var: "SIGN_IN", application: "frontend", path: "/sign-in/redirect")
9
+ end
10
+
11
+ # Find the GOV.UK URL for the "sign out" page
12
+ #
13
+ # @return [String] the URL
14
+ def self.sign_out
15
+ find_govuk_url(var: "SIGN_OUT", application: "frontend", path: "/sign-out")
16
+ end
17
+
18
+ # Find the GOV.UK URL for the "your account" page
19
+ #
20
+ # @return [String] the URL
21
+ def self.your_account
22
+ find_govuk_url(var: "YOUR_ACCOUNT", application: "frontend", path: "/account/home")
23
+ end
24
+
25
+ # Find the external URL for the "manage" page
26
+ #
27
+ # @return [String] the URL
28
+ def self.manage
29
+ find_external_url(var: "MANAGE", application: "account-manager", path: "/account/manage")
30
+ end
31
+
32
+ # Find the external URL for the "security" page
33
+ #
34
+ # @return [String] the URL
35
+ def self.security
36
+ find_external_url(var: "SECURITY", application: "account-manager", path: "/account/security")
37
+ end
38
+
39
+ # Find the external URL for the "feedback" page
40
+ #
41
+ # @return [String] the URL
42
+ def self.feedback
43
+ find_external_url(var: "FEEDBACK", application: "account-manager", path: "/feedback")
44
+ end
45
+
46
+ # Finds a URL on www.gov.uk. This method is used so we can have
47
+ # links which work both in production (where the website root is
48
+ # returned) and in local development (where an application URL is
49
+ # returned).
50
+ #
51
+ # If `GOVUK_PERSONALISATION_#{var}_URI` is in the environment, that
52
+ # will be returned.
53
+ #
54
+ # Otherwise, a `www.gov.uk` URL will be returned (a `dev.gov.uk`
55
+ # domain in development mode)
56
+ #
57
+ # @param var [String] the name of the variable to look up
58
+ # @param application [String] the name of the frontend application, passed to Plek, to use in development mode (if the env var is set, this is ignored)
59
+ # @param path [String] the path to use (if the env var is set this is ignored)
60
+ #
61
+ # @return [String] the URL
62
+ def self.find_govuk_url(var:, application:, path:)
63
+ value_from_env_var = ENV["GOVUK_PERSONALISATION_#{var}_URI"]
64
+ if value_from_env_var
65
+ value_from_env_var
66
+ else
67
+ plek_application_uri = Rails.env.development? ? Plek.find(application) : Plek.new.website_root
68
+ "#{plek_application_uri}#{path}"
69
+ end
70
+ end
71
+
72
+ # Finds a URL outside www.gov.uk
73
+ #
74
+ # If `GOVUK_PERSONALISATION_#{var}_URI` is in the environment, that
75
+ # will be returned.
76
+ #
77
+ # Otherwise, an application URL generated by Plek will be returned.
78
+ #
79
+ # @param var [String] the name of the variable to look up
80
+ # @param application [String] the name of the frontend application, passed to Plek (if the env var is set, this is ignored)
81
+ # @param path [String] the path to use (if the env var is set this is ignored)
82
+ #
83
+ # @return [String] the URL
84
+ def self.find_external_url(var:, application:, path:)
85
+ ENV.fetch("GOVUK_PERSONALISATION_#{var}_URI", "#{Plek.find(application)}#{path}")
86
+ end
87
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GovukPersonalisation
4
- VERSION = "0.4.0"
4
+ VERSION = "0.8.0"
5
5
  end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "govuk_personalisation/version"
4
- require "govuk_personalisation/account_concern"
4
+ require "govuk_personalisation/controller_concern"
5
+ require "govuk_personalisation/flash"
5
6
  require "govuk_personalisation/test_helpers/features"
6
7
  require "govuk_personalisation/test_helpers/requests"
8
+ require "govuk_personalisation/urls"
7
9
 
8
- module GovukPersonalisation
9
- class Error < StandardError; end
10
- # Your code goes here...
11
- end
10
+ module GovukPersonalisation; end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_personalisation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-08 00:00:00.000000000 Z
11
+ date: 2021-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: plek
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.9.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.9.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rails
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,20 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: climate_control
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: pry-byebug
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -116,9 +144,11 @@ files:
116
144
  - bin/setup
117
145
  - govuk_personalisation.gemspec
118
146
  - lib/govuk_personalisation.rb
119
- - lib/govuk_personalisation/account_concern.rb
147
+ - lib/govuk_personalisation/controller_concern.rb
148
+ - lib/govuk_personalisation/flash.rb
120
149
  - lib/govuk_personalisation/test_helpers/features.rb
121
150
  - lib/govuk_personalisation/test_helpers/requests.rb
151
+ - lib/govuk_personalisation/urls.rb
122
152
  - lib/govuk_personalisation/version.rb
123
153
  homepage: https://github.com/alphagov/govuk_personalisation
124
154
  licenses:
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/concern"
4
-
5
- module GovukPersonalisation
6
- module AccountConcern
7
- extend ActiveSupport::Concern
8
-
9
- ACCOUNT_SESSION_REQUEST_HEADER_NAME = "HTTP_GOVUK_ACCOUNT_SESSION"
10
- ACCOUNT_SESSION_RESPONSE_HEADER_NAME = "GOVUK-Account-Session"
11
- ACCOUNT_END_SESSION_RESPONSE_HEADER_NAME = "GOVUK-Account-End-Session"
12
- ACCOUNT_SESSION_DEV_COOKIE_NAME = "govuk_account_session"
13
-
14
- included do
15
- before_action :fetch_account_session_header
16
- before_action :set_account_vary_header
17
- attr_accessor :account_session_header
18
- end
19
-
20
- def logged_in?
21
- account_session_header.present?
22
- end
23
-
24
- def fetch_account_session_header
25
- @account_session_header =
26
- if request.headers[ACCOUNT_SESSION_REQUEST_HEADER_NAME]
27
- request.headers[ACCOUNT_SESSION_REQUEST_HEADER_NAME].presence
28
- elsif Rails.env.development?
29
- cookies[ACCOUNT_SESSION_DEV_COOKIE_NAME]
30
- end
31
- end
32
-
33
- def set_account_vary_header
34
- response.headers["Vary"] = [response.headers["Vary"], ACCOUNT_SESSION_RESPONSE_HEADER_NAME].compact.join(", ")
35
- end
36
-
37
- def set_account_session_header(govuk_account_session = nil)
38
- @account_session_header = govuk_account_session if govuk_account_session
39
- response.headers[ACCOUNT_SESSION_RESPONSE_HEADER_NAME] = @account_session_header
40
- if Rails.env.development?
41
- cookies[ACCOUNT_SESSION_DEV_COOKIE_NAME] = {
42
- value: @account_session_header,
43
- domain: "dev.gov.uk",
44
- }
45
- end
46
- end
47
-
48
- def logout!
49
- response.headers[ACCOUNT_END_SESSION_RESPONSE_HEADER_NAME] = "1"
50
- @account_session_header = nil
51
- if Rails.env.development?
52
- cookies[ACCOUNT_SESSION_DEV_COOKIE_NAME] = {
53
- value: "",
54
- domain: "dev.gov.uk",
55
- expires: 1.second.ago,
56
- }
57
- end
58
- end
59
- end
60
- end