liberty 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +1 -1
- data/README.md +51 -0
- data/lib/liberty/adapters/error_response.rb +30 -0
- data/lib/liberty/adapters/forbidden_response.rb +19 -0
- data/lib/liberty/adapters/request.rb +13 -5
- data/lib/liberty/adapters/response.rb +14 -0
- data/lib/liberty/adapters/unauthenticated_response.rb +29 -0
- data/lib/liberty/endpoint.rb +11 -0
- data/lib/liberty/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9450a8fadf0c6726d4249f2b6a62f4918045795f199be8ec8bec29412bbaebc1
|
|
4
|
+
data.tar.gz: 55ea606054dabaeec94add7f6b5fe63a5d38add6eca13dc9f4fc1ef2754ad5c2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 17e9cd7824c5b5038daee7fd5d3d441886865dc78562797cd502b8f96f6fd9cd48b2ed92da934265321757a2237e3682092e8653b1b4370260d73b3d9caef253
|
|
7
|
+
data.tar.gz: 7416e1f47f06cb05dd7fbaadc777f71bbac9bc828a5954f4db401c52e42abc88777ee3fd49ad1e24e22e412953e7d2b188b2a6983b153ea36f535f02a5f7d48c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [0.4.0] - 2026-09-15
|
|
2
|
+
|
|
3
|
+
### Breaking changes
|
|
4
|
+
|
|
5
|
+
- Endpoints now offer `authenticated?` and `authorized?` hooks for plugging in your own auth handlers. Both default to `false` to prevent endpoints from being exposed accidentally, so every existing endpoint responds with a 401 until it overrides them. Applications upgrading from older versions must override these methods, either to return `true` or with code that handles auth.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Responds with `401 Authentication required` when `authenticated?` returns `false`, and with `403 Forbidden` when `authorized?` returns `false`. The endpoint's status, headers, and content are only consulted when both return `true`.
|
|
10
|
+
- Adds a `www_authenticate_header` hook to endpoints. When it returns a challenge, such as `Bearer realm="api"`, 401 responses include it as the `WWW-Authenticate` header. It defaults to `nil`, which omits the header.
|
|
11
|
+
- Exposes the request's `Authorization` header as `request.headers[:authorization]` so endpoints can read credentials.
|
|
12
|
+
- Responds to `HEAD` requests that fail auth with an empty body and a `content-length` header, as the Rack specification requires.
|
|
13
|
+
|
|
1
14
|
## [0.3.1] - 2026-09-07
|
|
2
15
|
|
|
3
16
|
### Fixed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -50,6 +50,14 @@ Here's an example for an HTTP get to the `/hello` route, which returns a hello w
|
|
|
50
50
|
class MyEndpoint < Liberty::Endpoint
|
|
51
51
|
responds_to :get, '/hello'
|
|
52
52
|
|
|
53
|
+
def authenticated?
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def authorized?
|
|
58
|
+
true
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
def status
|
|
54
62
|
200
|
|
55
63
|
end
|
|
@@ -83,6 +91,49 @@ end
|
|
|
83
91
|
If you configure your CORS headers before you launch your application, `Endpoints` will
|
|
84
92
|
automatically respond with the right headers.
|
|
85
93
|
|
|
94
|
+
### Authentication & Authorization
|
|
95
|
+
|
|
96
|
+
Every `Endpoint` has two hooks: `#authenticated?` and `#authorized?`. Both default to `false`,
|
|
97
|
+
so an endpoint that does not implement them responds with `401 Authentication required`. Override
|
|
98
|
+
them to plug in your own auth. Liberty checks `#authenticated?` first and responds with a 401 when
|
|
99
|
+
it returns `false`. It then checks `#authorized?` and responds with `403 Forbidden` when that
|
|
100
|
+
returns `false`. Your endpoint's status, headers, and content are only consulted when both return
|
|
101
|
+
`true`. The raw `Authorization` header is available as `request.headers[:authorization]`.
|
|
102
|
+
|
|
103
|
+
A 401 response should tell the client how to authenticate. Override `#www_authenticate_header`
|
|
104
|
+
to return the challenge, such as `Bearer realm="api"`, and Liberty adds it as the
|
|
105
|
+
`WWW-Authenticate` header on 401 responses. When it returns `nil` the header is omitted.
|
|
106
|
+
|
|
107
|
+
Here's an example of how to use `#authenticated?` and `#authorized?`:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
class YourEndpoint < Liberty::Endpoint
|
|
111
|
+
responds_to :get, "/your_endpoint"
|
|
112
|
+
|
|
113
|
+
def authenticated?
|
|
114
|
+
!current_user.nil?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def www_authenticate_header
|
|
118
|
+
'Bearer realm="api"'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def authorized?
|
|
122
|
+
current_user.admin?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def current_user
|
|
126
|
+
@current_user ||= Sessions.find_by_token(bearer_token)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def bearer_token
|
|
132
|
+
request.headers[:authorization].to_s.delete_prefix("Bearer ")
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
```
|
|
136
|
+
|
|
86
137
|
## Installation
|
|
87
138
|
|
|
88
139
|
Install the gem and add to the application's Gemfile by executing:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Liberty
|
|
4
|
+
module Adapters
|
|
5
|
+
class ErrorResponse
|
|
6
|
+
attr_reader :endpoint
|
|
7
|
+
|
|
8
|
+
def initialize(endpoint)
|
|
9
|
+
@endpoint = endpoint
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_rack_response
|
|
13
|
+
[status, headers, rack_body]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def headers
|
|
19
|
+
{
|
|
20
|
+
Response::CONTENT_LENGTH => body.bytesize.to_s,
|
|
21
|
+
Response::CONTENT_TYPE => Response::MIME_TYPE_TEXT
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def rack_body
|
|
26
|
+
endpoint.request.head? ? [] : [body]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "error_response"
|
|
4
|
+
|
|
5
|
+
module Liberty
|
|
6
|
+
module Adapters
|
|
7
|
+
class ForbiddenResponse < ErrorResponse
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def status
|
|
11
|
+
403
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def body
|
|
15
|
+
"Forbidden"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -7,6 +7,7 @@ module Liberty
|
|
|
7
7
|
class Request
|
|
8
8
|
PARSED_BODY = "parsed_body"
|
|
9
9
|
ROUTER_PARAMS = "router.params"
|
|
10
|
+
AUTHORIZATION = "HTTP_AUTHORIZATION"
|
|
10
11
|
|
|
11
12
|
attr_reader :env
|
|
12
13
|
|
|
@@ -15,10 +16,7 @@ module Liberty
|
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def headers
|
|
18
|
-
@headers ||= env ? {
|
|
19
|
-
accept_media_types: accept_media_types,
|
|
20
|
-
preferred_media_type: preferred_media_type
|
|
21
|
-
} : {}
|
|
19
|
+
@headers ||= env ? env_headers : {}
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def params
|
|
@@ -31,6 +29,14 @@ module Liberty
|
|
|
31
29
|
|
|
32
30
|
private
|
|
33
31
|
|
|
32
|
+
def env_headers
|
|
33
|
+
{
|
|
34
|
+
accept_media_types: accept_media_types,
|
|
35
|
+
preferred_media_type: preferred_media_type,
|
|
36
|
+
authorization: env[AUTHORIZATION]
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
def all_params
|
|
35
41
|
form_params.merge!(body_params).merge!(url_params)
|
|
36
42
|
end
|
|
@@ -73,7 +79,9 @@ module Liberty
|
|
|
73
79
|
end
|
|
74
80
|
|
|
75
81
|
def symbolize_keys(hash)
|
|
76
|
-
hash.each_with_object({})
|
|
82
|
+
hash.each_with_object({}) do |(key, value), obj|
|
|
83
|
+
obj[key.to_sym] = value
|
|
84
|
+
end
|
|
77
85
|
end
|
|
78
86
|
|
|
79
87
|
def rack_request
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "forbidden_response"
|
|
4
|
+
require_relative "unauthenticated_response"
|
|
5
|
+
|
|
3
6
|
module Liberty
|
|
4
7
|
module Adapters
|
|
5
8
|
class Response
|
|
@@ -17,11 +20,22 @@ module Liberty
|
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
def to_rack_response
|
|
23
|
+
return unauthenticated_response unless endpoint.authenticated?
|
|
24
|
+
return forbidden_response unless endpoint.authorized?
|
|
25
|
+
|
|
20
26
|
[status, headers, rack_body]
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
private
|
|
24
30
|
|
|
31
|
+
def unauthenticated_response
|
|
32
|
+
UnauthenticatedResponse.new(endpoint).to_rack_response
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def forbidden_response
|
|
36
|
+
ForbiddenResponse.new(endpoint).to_rack_response
|
|
37
|
+
end
|
|
38
|
+
|
|
25
39
|
def status
|
|
26
40
|
endpoint.status
|
|
27
41
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "error_response"
|
|
4
|
+
|
|
5
|
+
module Liberty
|
|
6
|
+
module Adapters
|
|
7
|
+
class UnauthenticatedResponse < ErrorResponse
|
|
8
|
+
WWW_AUTHENTICATE = "www-authenticate"
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def status
|
|
13
|
+
401
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def headers
|
|
17
|
+
challenge ? super.merge(WWW_AUTHENTICATE => challenge) : super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def challenge
|
|
21
|
+
endpoint.www_authenticate_header
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def body
|
|
25
|
+
"Authentication required"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/liberty/endpoint.rb
CHANGED
data/lib/liberty/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: liberty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alan Ridlehoover
|
|
@@ -116,11 +116,14 @@ files:
|
|
|
116
116
|
- Rakefile
|
|
117
117
|
- assets/liberty.png
|
|
118
118
|
- lib/liberty.rb
|
|
119
|
+
- lib/liberty/adapters/error_response.rb
|
|
120
|
+
- lib/liberty/adapters/forbidden_response.rb
|
|
119
121
|
- lib/liberty/adapters/parsers/factory.rb
|
|
120
122
|
- lib/liberty/adapters/parsers/json.rb
|
|
121
123
|
- lib/liberty/adapters/parsers/null.rb
|
|
122
124
|
- lib/liberty/adapters/request.rb
|
|
123
125
|
- lib/liberty/adapters/response.rb
|
|
126
|
+
- lib/liberty/adapters/unauthenticated_response.rb
|
|
124
127
|
- lib/liberty/application.rb
|
|
125
128
|
- lib/liberty/cors.rb
|
|
126
129
|
- lib/liberty/cors/middleware.rb
|