Skip to content

Commit 833f534

Browse files
authored
Merge pull request #23 from tayloraswift/secure-procedural-endpoints
add authentication/authorization enforcement to all procedural endpoints
2 parents 9f422e7 + 137e701 commit 833f534

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1086
-466
lines changed

Assets/css/Admin.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/css/Admin.css.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div align="center">
22

3-
<strong><em><code>unidoc</code></em></strong><br><small><code>0.2.6</code></small>
3+
<strong><em><code>unidoc</code></em></strong><br><small><code>0.2.7</code></small>
44

55
[![ci build status](https://github.com/kelvin13/swift-unidoc/actions/workflows/build.yml/badge.svg)](https://github.com/kelvin13/swift-unidoc/actions/workflows/build.yml)
66

Sources/GitHubAPI/GitHubRateLimitError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ public
44
protocol GitHubRateLimitError:Error, Equatable, Sendable
55
{
66
/// The UTC epoch second when the rate limit will reset.
7-
var until:UnixTime { get }
7+
var until:UnixInstant { get }
88
}

Sources/GitHubClient/GitHubClient.RateLimitError.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ extension GitHubClient
77
struct RateLimitError:GitHubRateLimitError, Equatable, Sendable
88
{
99
public
10-
let until:UnixTime
10+
let until:UnixInstant
1111

1212
@inlinable internal
13-
init(until:UnixTime)
13+
init(until:UnixInstant)
1414
{
1515
self.until = until
1616
}

Sources/HTTP/ServerResource.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ struct ServerResource:Equatable, Sendable
2222
self.hash = hash
2323
}
2424
}
25+
extension ServerResource:ExpressibleByStringLiteral
26+
{
27+
@inlinable public
28+
init(stringLiteral:String)
29+
{
30+
self.init(content: .string(stringLiteral), type: .text(.plain, charset: .utf8))
31+
}
32+
}
2533
extension ServerResource
2634
{
2735
/// Computes and populates the resource ``hash`` if it has not already been computed, and

Sources/HTTP/ServerResponse.swift

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,24 @@ import Media
33
@frozen public
44
enum ServerResponse:Equatable, Sendable
55
{
6-
case error (ServerResource)
7-
case forbidden (ServerResource)
8-
case ok (ServerResource)
9-
case multiple (ServerResource)
10-
case notFound (ServerResource)
6+
/// 200 OK.
7+
case ok (ServerResource)
8+
/// 300 Multiple Choices.
9+
case multiple (ServerResource)
10+
/// 400 Bad Request.
11+
case badRequest (ServerResource)
12+
/// 401 Unauthorized.
13+
case unauthorized (ServerResource)
14+
/// 403 Forbidden.
15+
case forbidden (ServerResource)
16+
/// 404 Not Found.
17+
case notFound (ServerResource)
18+
/// 409 Conflict.
19+
case conflict (ServerResource)
20+
/// 500 Internal Server Error.
21+
case error (ServerResource)
1122

12-
case redirect (ServerRedirect, cookies:[Cookie] = [])
23+
case redirect (ServerRedirect, cookies:[Cookie] = [])
1324
}
1425
extension ServerResponse
1526
{
@@ -26,12 +37,15 @@ extension ServerResponse
2637
{
2738
switch self
2839
{
29-
case .redirect: return 0
30-
case .error (let resource): return resource.content.size
31-
case .forbidden (let resource): return resource.content.size
32-
case .ok (let resource): return resource.content.size
33-
case .multiple (let resource): return resource.content.size
34-
case .notFound (let resource): return resource.content.size
40+
case .redirect: return 0
41+
case .ok (let resource): return resource.content.size
42+
case .multiple (let resource): return resource.content.size
43+
case .badRequest (let resource): return resource.content.size
44+
case .unauthorized (let resource): return resource.content.size
45+
case .forbidden (let resource): return resource.content.size
46+
case .notFound (let resource): return resource.content.size
47+
case .conflict (let resource): return resource.content.size
48+
case .error (let resource): return resource.content.size
3549
}
3650
}
3751
}

Sources/HTTPServer/Channels/ServerMessage.swift

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,32 @@ extension ServerMessage
2424
{
2525
switch response
2626
{
27+
case .ok(let resource):
28+
self.init(resource: resource, using: allocator, as: .ok)
29+
30+
case .multiple(let resource):
31+
self.init(resource: resource, using: allocator, as: .multipleChoices)
32+
2733
case .redirect(let redirect, cookies: let cookies):
2834
self.init(redirect: redirect, cookies: cookies)
2935

30-
case .error(let resource):
31-
self.init(resource: resource, using: allocator, as: .internalServerError)
36+
case .badRequest(let resource):
37+
self.init(resource: resource, using: allocator, as: .badRequest)
38+
39+
case .unauthorized(let resource):
40+
self.init(resource: resource, using: allocator, as: .unauthorized)
3241

3342
case .forbidden(let resource):
3443
self.init(resource: resource, using: allocator, as: .forbidden)
3544

36-
case .ok(let resource):
37-
self.init(resource: resource, using: allocator, as: .ok)
38-
39-
case .multiple(let resource):
40-
self.init(resource: resource, using: allocator, as: .multipleChoices)
41-
4245
case .notFound(let resource):
4346
self.init(resource: resource, using: allocator, as: .notFound)
47+
48+
case .conflict(let resource):
49+
self.init(resource: resource, using: allocator, as: .conflict)
50+
51+
case .error(let resource):
52+
self.init(resource: resource, using: allocator, as: .internalServerError)
4453
}
4554
}
4655

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import BSONDecoding
2+
import MongoQL
3+
4+
extension Account
5+
{
6+
@frozen public
7+
struct Cookie:Equatable, Hashable, Sendable
8+
{
9+
@usableFromInline internal
10+
let id:Account.ID
11+
@usableFromInline internal
12+
let cookie:Int64
13+
14+
@inlinable internal
15+
init(id:Account.ID, cookie:Int64)
16+
{
17+
self.id = id
18+
self.cookie = cookie
19+
}
20+
}
21+
}
22+
extension Account.Cookie:CustomStringConvertible
23+
{
24+
@inlinable public
25+
var description:String { "\(self.id):\(UInt64.init(bitPattern: self.cookie))" }
26+
}
27+
extension Account.Cookie:LosslessStringConvertible
28+
{
29+
@inlinable public
30+
init?(_ description:some StringProtocol)
31+
{
32+
if let colon:String.Index = description.firstIndex(of: ":"),
33+
let id:Account.ID = .init(description[..<colon]),
34+
let cookie:UInt64 = .init(description[description.index(after: colon)...])
35+
{
36+
self.init(id: id, cookie: .init(bitPattern: cookie))
37+
}
38+
else
39+
{
40+
return nil
41+
}
42+
}
43+
}
44+
extension Account.Cookie:BSONDocumentDecodable
45+
{
46+
public
47+
typealias CodingKey = Account.CodingKey
48+
49+
@inlinable public
50+
init(bson:BSON.DocumentDecoder<CodingKey, some RandomAccessCollection<UInt8>>) throws
51+
{
52+
self.init(id: try bson[.id].decode(), cookie: try bson[.cookie].decode())
53+
}
54+
}

Sources/UnidocDB/Accounts/Account.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ struct Account:Identifiable, Sendable
2525
}
2626
extension Account
2727
{
28+
@inlinable public static
29+
func machine(_ number:Int32 = 0) -> Self
30+
{
31+
.init(id: .machine(number), role: .machine)
32+
}
33+
2834
@inlinable public static
2935
func github(user:GitHub.User, role:Role) -> Self
3036
{
@@ -39,7 +45,7 @@ extension Account:MongoMasterCodingModel
3945
case id = "_id"
4046

4147
/// The session cookie associated with this account, if logged in. This is generated
42-
/// randomly in ``AccountDatabase.update(account:with:)``.
48+
/// randomly in ``AccountDatabase.Users.update(account:with:)``.
4349
case cookie
4450

4551
case role

0 commit comments

Comments
 (0)