tangled
alpha
login
or
join now
julien.rbrt.fr
/
vow
forked from
hailey.at/cocoon
0
fork
atom
Vow, uncensorable PDS written in Go
0
fork
atom
overview
issues
pulls
pipelines
build: bump to go 1.26
julien.rbrt.fr
2 days ago
49f2e2fe
6f8100fe
1/1
ci.yml
success
1min 12s
+119
-149
27 changed files
expand all
collapse all
unified
split
Dockerfile
go.mod
go.sum
models
models.go
server
handle_identity_sign_plc_operation.go
handle_identity_update_handle.go
handle_oauth_authorize.go
handle_oauth_par.go
handle_oauth_token.go
handle_proxy_get_feed.go
handle_repo_describe_repo.go
handle_repo_list_records.go
handle_server_confirm_email.go
handle_server_create_account.go
handle_server_create_invite_codes.go
handle_server_create_session.go
handle_server_deactivate_account.go
handle_server_delete_account.go
handle_server_get_service_auth.go
handle_server_request_email_confirmation.go
handle_server_reset_password.go
handle_server_resolve_handle.go
handle_sync_get_blob.go
handle_sync_list_blobs.go
handle_well_known.go
middleware.go
repo.go
+2
-2
Dockerfile
···
1
1
### Compile stage
2
2
-
FROM golang:1.25.1-bookworm AS build-env
2
2
+
FROM golang:1.26.1-bookworm AS build-env
3
3
4
4
ADD . /dockerbuild
5
5
WORKDIR /dockerbuild
···
22
22
23
23
LABEL org.opencontainers.image.source=https://pkg.rbrt.fr/vow
24
24
LABEL org.opencontainers.image.description="Vow ATProto PDS"
25
25
-
LABEL org.opencontainers.image.licenses=MIT
25
25
+
LABEL org.opencontainers.image.licenses=MIT
+1
-3
go.mod
···
1
1
module pkg.rbrt.fr/vow
2
2
3
3
-
go 1.25
3
3
+
go 1.26.1
4
4
5
5
require (
6
6
-
github.com/Azure/go-autorest/autorest/to v0.4.1
7
6
github.com/bluesky-social/indigo v0.0.0-20260203235305-a86f3ae1f8ec
8
7
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792
9
8
github.com/domodwyer/mailyak/v3 v3.6.2
···
36
35
)
37
36
38
37
require (
39
39
-
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
40
38
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b // indirect
41
39
github.com/beorn7/perks v1.0.1 // indirect
42
40
github.com/cespare/xxhash/v2 v2.3.0 // indirect
-4
go.sum
···
1
1
-
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
2
2
-
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
3
3
-
github.com/Azure/go-autorest/autorest/to v0.4.1 h1:CxNHBqdzTr7rLtdrtb5CMjJcDut+WNGCVv7OmS5+lTc=
4
4
-
github.com/Azure/go-autorest/autorest/to v0.4.1/go.mod h1:EtaofgU4zmtvn1zT2ARsjRFdq9vXx0YWtmElwL+GZ9M=
5
1
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
6
2
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b h1:5/++qT1/z812ZqBvqQt6ToRswSuPZ/B33m6xVHRzADU=
7
3
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b/go.mod h1:4+EPqMRApwwE/6yo6CxiHoSnBzjRr3jsqer7frxP8y4=
+1
-2
models/models.go
···
4
4
"context"
5
5
"time"
6
6
7
7
-
"github.com/Azure/go-autorest/autorest/to"
8
7
"github.com/bluesky-social/indigo/atproto/atcrypto"
9
8
)
10
9
···
58
57
func (r *Repo) Status() *string {
59
58
var status *string
60
59
if r.Deactivated {
61
61
-
status = to.StringPtr("deactivated")
60
60
+
status = new("deactivated")
62
61
}
63
62
return status
64
63
}
+1
-2
server/handle_identity_sign_plc_operation.go
···
7
7
"strings"
8
8
"time"
9
9
10
10
-
"github.com/Azure/go-autorest/autorest/to"
11
10
"github.com/bluesky-social/indigo/atproto/atcrypto"
12
11
"pkg.rbrt.fr/vow/identity"
13
12
"pkg.rbrt.fr/vow/internal/helpers"
···
45
44
}
46
45
47
46
if repo.PlcOperationCode == nil || repo.PlcOperationCodeExpiresAt == nil {
48
48
-
helpers.InputError(w, to.StringPtr("InvalidToken"))
47
47
+
helpers.InputError(w, new("InvalidToken"))
49
48
return
50
49
}
51
50
+1
-2
server/handle_identity_update_handle.go
···
7
7
"strings"
8
8
"time"
9
9
10
10
-
"github.com/Azure/go-autorest/autorest/to"
11
10
"github.com/bluesky-social/indigo/api/atproto"
12
11
"github.com/bluesky-social/indigo/atproto/atcrypto"
13
12
"github.com/bluesky-social/indigo/events"
···
97
96
if err := s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{
98
97
RepoIdentity: &atproto.SyncSubscribeRepos_Identity{
99
98
Did: repo.Repo.Did,
100
100
-
Handle: to.StringPtr(req.Handle),
99
99
+
Handle: new(req.Handle),
101
100
Seq: time.Now().UnixMicro(), // TODO: no
102
101
Time: time.Now().Format(util.ISO8601),
103
102
},
+15
-16
server/handle_oauth_authorize.go
···
7
7
"strings"
8
8
"time"
9
9
10
10
-
"github.com/Azure/go-autorest/autorest/to"
11
10
"pkg.rbrt.fr/vow/internal/helpers"
12
11
"pkg.rbrt.fr/vow/oauth"
13
12
"pkg.rbrt.fr/vow/oauth/constants"
···
30
29
id, err := oauth.DecodeRequestUri(requestUri)
31
30
if err != nil {
32
31
logger.Error("no request uri found in input", "url", r.URL.String())
33
33
-
helpers.InputError(w, to.StringPtr("no request uri"))
32
32
+
helpers.InputError(w, new("no request uri"))
34
33
return
35
34
}
36
35
reqId = id
···
46
45
CodeChallengeMethod: r.URL.Query().Get("code_challenge_method"),
47
46
}
48
47
if v := r.URL.Query().Get("code_challenge"); v != "" {
49
49
-
parRequest.CodeChallenge = to.StringPtr(v)
48
48
+
parRequest.CodeChallenge = new(v)
50
49
}
51
50
if v := r.URL.Query().Get("login_hint"); v != "" {
52
52
-
parRequest.LoginHint = to.StringPtr(v)
51
51
+
parRequest.LoginHint = new(v)
53
52
}
54
53
if v := r.URL.Query().Get("dpop_jkt"); v != "" {
55
55
-
parRequest.DpopJkt = to.StringPtr(v)
54
54
+
parRequest.DpopJkt = new(v)
56
55
}
57
56
if v := r.URL.Query().Get("response_mode"); v != "" {
58
58
-
parRequest.ResponseMode = to.StringPtr(v)
57
57
+
parRequest.ResponseMode = new(v)
59
58
}
60
59
61
60
if err := s.validator.Struct(parRequest); err != nil {
···
71
70
}
72
71
return
73
72
}
74
74
-
helpers.InputError(w, to.StringPtr("no request uri and invalid parameters"))
73
73
+
helpers.InputError(w, new("no request uri and invalid parameters"))
75
74
return
76
75
}
77
76
···
80
79
})
81
80
if err != nil {
82
81
s.logger.Error("error authenticating client in standard request", "client_id", parRequest.ClientID, "error", err)
83
83
-
helpers.ServerError(w, to.StringPtr(err.Error()))
82
82
+
helpers.ServerError(w, new(err.Error()))
84
83
return
85
84
}
86
85
···
121
120
122
121
var req provider.OauthAuthorizationRequest
123
122
if err := s.db.Raw(ctx, "SELECT * FROM oauth_authorization_requests WHERE request_id = ?", nil, reqId).Scan(&req).Error; err != nil {
124
124
-
helpers.ServerError(w, to.StringPtr(err.Error()))
123
123
+
helpers.ServerError(w, new(err.Error()))
125
124
return
126
125
}
127
126
128
127
clientId := r.URL.Query().Get("client_id")
129
128
if clientId != req.ClientId {
130
130
-
helpers.InputError(w, to.StringPtr("client id does not match the client id for the supplied request"))
129
129
+
helpers.InputError(w, new("client id does not match the client id for the supplied request"))
131
130
return
132
131
}
133
132
134
133
client, err := s.oauthProvider.ClientManager.GetClient(r.Context(), req.ClientId)
135
134
if err != nil {
136
136
-
helpers.ServerError(w, to.StringPtr(err.Error()))
135
135
+
helpers.ServerError(w, new(err.Error()))
137
136
return
138
137
}
139
138
···
181
180
182
181
reqId, err := oauth.DecodeRequestUri(req.RequestUri)
183
182
if err != nil {
184
184
-
helpers.InputError(w, to.StringPtr(err.Error()))
183
183
+
helpers.InputError(w, new(err.Error()))
185
184
return
186
185
}
187
186
188
187
var authReq provider.OauthAuthorizationRequest
189
188
if err := s.db.Raw(ctx, "SELECT * FROM oauth_authorization_requests WHERE request_id = ?", nil, reqId).Scan(&authReq).Error; err != nil {
190
190
-
helpers.ServerError(w, to.StringPtr(err.Error()))
189
189
+
helpers.ServerError(w, new(err.Error()))
191
190
return
192
191
}
193
192
194
193
client, err := s.oauthProvider.ClientManager.GetClient(r.Context(), authReq.ClientId)
195
194
if err != nil {
196
196
-
helpers.ServerError(w, to.StringPtr(err.Error()))
195
195
+
helpers.ServerError(w, new(err.Error()))
197
196
return
198
197
}
199
198
···
204
203
}
205
204
206
205
if time.Now().After(authReq.ExpiresAt) {
207
207
-
helpers.InputError(w, to.StringPtr("the request has expired"))
206
206
+
helpers.InputError(w, new("the request has expired"))
208
207
return
209
208
}
210
209
211
210
if authReq.Sub != nil || authReq.Code != nil {
212
212
-
helpers.InputError(w, to.StringPtr("this request was already authorized"))
211
211
+
helpers.InputError(w, new("this request was already authorized"))
213
212
return
214
213
}
215
214
+8
-9
server/handle_oauth_par.go
···
5
5
"net/http"
6
6
"time"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
9
8
"pkg.rbrt.fr/vow/internal/helpers"
10
9
"pkg.rbrt.fr/vow/oauth"
11
10
"pkg.rbrt.fr/vow/oauth/constants"
···
39
38
CodeChallengeMethod: r.FormValue("code_challenge_method"),
40
39
}
41
40
if v := r.FormValue("code_challenge"); v != "" {
42
42
-
parRequest.CodeChallenge = to.StringPtr(v)
41
41
+
parRequest.CodeChallenge = new(v)
43
42
}
44
43
if v := r.FormValue("login_hint"); v != "" {
45
45
-
parRequest.LoginHint = to.StringPtr(v)
44
44
+
parRequest.LoginHint = new(v)
46
45
}
47
46
if v := r.FormValue("dpop_jkt"); v != "" {
48
48
-
parRequest.DpopJkt = to.StringPtr(v)
47
47
+
parRequest.DpopJkt = new(v)
49
48
}
50
49
if v := r.FormValue("response_mode"); v != "" {
51
51
-
parRequest.ResponseMode = to.StringPtr(v)
50
50
+
parRequest.ResponseMode = new(v)
52
51
}
53
52
if v := r.FormValue("client_assertion_type"); v != "" {
54
54
-
parRequest.ClientAssertionType = to.StringPtr(v)
53
53
+
parRequest.ClientAssertionType = new(v)
55
54
}
56
55
if v := r.FormValue("client_assertion"); v != "" {
57
57
-
parRequest.ClientAssertion = to.StringPtr(v)
56
56
+
parRequest.ClientAssertion = new(v)
58
57
}
59
58
60
59
if err := s.validator.Struct(parRequest); err != nil {
···
90
89
})
91
90
if err != nil {
92
91
logger.Error("error authenticating client", "client_id", parRequest.ClientID, "error", err)
93
93
-
helpers.InputError(w, to.StringPtr(err.Error()))
92
92
+
helpers.InputError(w, new(err.Error()))
94
93
return
95
94
}
96
95
97
96
if parRequest.DpopJkt == nil {
98
97
if client.Metadata.DpopBoundAccessTokens {
99
99
-
parRequest.DpopJkt = to.StringPtr(dpopProof.JKT)
98
98
+
parRequest.DpopJkt = new(dpopProof.JKT)
100
99
}
101
100
} else {
102
101
if !client.Metadata.DpopBoundAccessTokens {
+26
-27
server/handle_oauth_token.go
···
10
10
"slices"
11
11
"time"
12
12
13
13
-
"github.com/Azure/go-autorest/autorest/to"
14
13
"github.com/golang-jwt/jwt/v4"
15
14
"pkg.rbrt.fr/vow/internal/helpers"
16
15
"pkg.rbrt.fr/vow/oauth"
···
51
50
GrantType: r.FormValue("grant_type"),
52
51
}
53
52
if v := r.FormValue("code"); v != "" {
54
54
-
req.Code = to.StringPtr(v)
53
53
+
req.Code = new(v)
55
54
}
56
55
if v := r.FormValue("code_verifier"); v != "" {
57
57
-
req.CodeVerifier = to.StringPtr(v)
56
56
+
req.CodeVerifier = new(v)
58
57
}
59
58
if v := r.FormValue("redirect_uri"); v != "" {
60
60
-
req.RedirectURI = to.StringPtr(v)
59
59
+
req.RedirectURI = new(v)
61
60
}
62
61
if v := r.FormValue("refresh_token"); v != "" {
63
63
-
req.RefreshToken = to.StringPtr(v)
62
62
+
req.RefreshToken = new(v)
64
63
}
65
64
if v := r.FormValue("client_assertion_type"); v != "" {
66
66
-
req.ClientAssertionType = to.StringPtr(v)
65
65
+
req.ClientAssertionType = new(v)
67
66
}
68
67
if v := r.FormValue("client_assertion"); v != "" {
69
69
-
req.ClientAssertion = to.StringPtr(v)
68
68
+
req.ClientAssertion = new(v)
70
69
}
71
70
req.AuthenticateClientRequestBase = provider.AuthenticateClientRequestBase{
72
71
ClientID: r.FormValue("client_id"),
···
97
96
})
98
97
if err != nil {
99
98
logger.Error("error authenticating client", "client_id", req.ClientID, "error", err)
100
100
-
helpers.InputError(w, to.StringPtr(err.Error()))
99
99
+
helpers.InputError(w, new(err.Error()))
101
100
return
102
101
}
103
102
104
103
// TODO: this should come from an oauth provider config
105
104
if !slices.Contains([]string{"authorization_code", "refresh_token"}, req.GrantType) {
106
106
-
helpers.InputError(w, to.StringPtr(fmt.Sprintf(`"%s" grant type is not supported by the server`, req.GrantType)))
105
105
+
helpers.InputError(w, new(fmt.Sprintf(`"%s" grant type is not supported by the server`, req.GrantType)))
107
106
return
108
107
}
109
108
110
109
if !slices.Contains(client.Metadata.GrantTypes, req.GrantType) {
111
111
-
helpers.InputError(w, to.StringPtr(fmt.Sprintf(`"%s" grant type is not supported by the client`, req.GrantType)))
110
110
+
helpers.InputError(w, new(fmt.Sprintf(`"%s" grant type is not supported by the client`, req.GrantType)))
112
111
return
113
112
}
114
113
115
114
if req.GrantType == "authorization_code" {
116
115
if req.Code == nil {
117
117
-
helpers.InputError(w, to.StringPtr(`"code" is required"`))
116
116
+
helpers.InputError(w, new(`"code" is required"`))
118
117
return
119
118
}
120
119
···
127
126
}
128
127
129
128
if req.RedirectURI == nil || *req.RedirectURI != authReq.Parameters.RedirectURI {
130
130
-
helpers.InputError(w, to.StringPtr(`"redirect_uri" mismatch`))
129
129
+
helpers.InputError(w, new(`"redirect_uri" mismatch`))
131
130
return
132
131
}
133
132
134
133
if authReq.Parameters.CodeChallenge != nil {
135
134
if req.CodeVerifier == nil {
136
136
-
helpers.InputError(w, to.StringPtr(`"code_verifier" is required`))
135
135
+
helpers.InputError(w, new(`"code_verifier" is required`))
137
136
return
138
137
}
139
138
140
139
if len(*req.CodeVerifier) < 43 {
141
141
-
helpers.InputError(w, to.StringPtr(`"code_verifier" is too short`))
140
140
+
helpers.InputError(w, new(`"code_verifier" is too short`))
142
141
return
143
142
}
144
143
145
144
switch authReq.Parameters.CodeChallengeMethod {
146
145
case "", "plain":
147
146
if authReq.Parameters.CodeChallenge != req.CodeVerifier {
148
148
-
helpers.InputError(w, to.StringPtr("invalid code_verifier"))
147
147
+
helpers.InputError(w, new("invalid code_verifier"))
149
148
return
150
149
}
151
150
case "S256":
···
161
160
compdChal := h.Sum(nil)
162
161
163
162
if !bytes.Equal(inputChal, compdChal) {
164
164
-
helpers.InputError(w, to.StringPtr("invalid code_verifier"))
163
163
+
helpers.InputError(w, new("invalid code_verifier"))
165
164
return
166
165
}
167
166
default:
168
168
-
helpers.InputError(w, to.StringPtr("unsupported code_challenge_method "+authReq.Parameters.CodeChallengeMethod))
167
167
+
helpers.InputError(w, new("unsupported code_challenge_method "+authReq.Parameters.CodeChallengeMethod))
169
168
return
170
169
}
171
170
} else if req.CodeVerifier != nil {
172
172
-
helpers.InputError(w, to.StringPtr("code_challenge parameter wasn't provided"))
171
171
+
helpers.InputError(w, new("code_challenge parameter wasn't provided"))
173
172
return
174
173
}
175
174
176
175
repo, err := s.getRepoActorByDid(ctx, *authReq.Sub)
177
176
if err != nil {
178
178
-
helpers.InputError(w, to.StringPtr("unable to find actor"))
177
177
+
helpers.InputError(w, new("unable to find actor"))
179
178
return
180
179
}
181
180
···
242
241
243
242
if req.GrantType == "refresh_token" {
244
243
if req.RefreshToken == nil {
245
245
-
helpers.InputError(w, to.StringPtr(`"refresh_token" is required`))
244
244
+
helpers.InputError(w, new(`"refresh_token" is required`))
246
245
return
247
246
}
248
247
···
254
253
}
255
254
256
255
if client.Metadata.ClientID != oauthToken.ClientId {
257
257
-
helpers.InputError(w, to.StringPtr(`"client_id" mismatch`))
256
256
+
helpers.InputError(w, new(`"client_id" mismatch`))
258
257
return
259
258
}
260
259
261
260
if clientAuth.Method != oauthToken.ClientAuth.Method {
262
262
-
helpers.InputError(w, to.StringPtr(`"client authentication method mismatch`))
261
261
+
helpers.InputError(w, new(`"client authentication method mismatch`))
263
262
return
264
263
}
265
264
266
265
if *oauthToken.Parameters.DpopJkt != proof.JKT {
267
267
-
helpers.InputError(w, to.StringPtr("dpop proof does not match expected jkt"))
266
266
+
helpers.InputError(w, new("dpop proof does not match expected jkt"))
268
267
return
269
268
}
270
269
271
270
ageRes := oauth.GetSessionAgeFromToken(oauthToken)
272
271
273
272
if ageRes.SessionExpired {
274
274
-
helpers.InputError(w, to.StringPtr("Session expired"))
273
273
+
helpers.InputError(w, new("Session expired"))
275
274
return
276
275
}
277
276
278
277
if ageRes.RefreshExpired {
279
279
-
helpers.InputError(w, to.StringPtr("Refresh token expired"))
278
278
+
helpers.InputError(w, new("Refresh token expired"))
280
279
return
281
280
}
282
281
283
282
if client.Metadata.DpopBoundAccessTokens && oauthToken.Parameters.DpopJkt == nil {
284
283
// why? ref impl
285
285
-
helpers.InputError(w, to.StringPtr("dpop jkt is required for dpop bound access tokens"))
284
284
+
helpers.InputError(w, new("dpop jkt is required for dpop bound access tokens"))
286
285
return
287
286
}
288
287
···
336
335
return
337
336
}
338
337
339
339
-
helpers.InputError(w, to.StringPtr(fmt.Sprintf(`grant type "%s" is not supported`, req.GrantType)))
338
338
+
helpers.InputError(w, new(fmt.Sprintf(`grant type "%s" is not supported`, req.GrantType)))
340
339
}
+1
-2
server/handle_proxy_get_feed.go
···
4
4
"context"
5
5
"net/http"
6
6
7
7
-
"github.com/Azure/go-autorest/autorest/to"
8
7
"github.com/bluesky-social/indigo/api/atproto"
9
8
"github.com/bluesky-social/indigo/api/bsky"
10
9
"github.com/bluesky-social/indigo/atproto/syntax"
···
15
14
func (s *Server) handleProxyBskyFeedGetFeed(w http.ResponseWriter, r *http.Request) {
16
15
feedUri, err := syntax.ParseATURI(r.URL.Query().Get("feed"))
17
16
if err != nil {
18
18
-
helpers.InputError(w, to.StringPtr("invalid feed uri"))
17
17
+
helpers.InputError(w, new("invalid feed uri"))
19
18
return
20
19
}
21
20
+1
-2
server/handle_repo_describe_repo.go
···
4
4
"net/http"
5
5
"strings"
6
6
7
7
-
"github.com/Azure/go-autorest/autorest/to"
8
7
"gorm.io/gorm"
9
8
"pkg.rbrt.fr/vow/identity"
10
9
"pkg.rbrt.fr/vow/internal/helpers"
···
27
26
repo, err := s.getRepoActorByDid(ctx, did)
28
27
if err != nil {
29
28
if err == gorm.ErrRecordNotFound {
30
30
-
helpers.InputError(w, to.StringPtr("RepoNotFound"))
29
29
+
helpers.InputError(w, new("RepoNotFound"))
31
30
return
32
31
}
33
32
+2
-3
server/handle_repo_list_records.go
···
4
4
"net/http"
5
5
"strconv"
6
6
7
7
-
"github.com/Azure/go-autorest/autorest/to"
8
7
"github.com/bluesky-social/indigo/atproto/atdata"
9
8
"github.com/bluesky-social/indigo/atproto/syntax"
10
9
"pkg.rbrt.fr/vow/internal/helpers"
···
87
86
if _, err := syntax.ParseDID(did); err != nil {
88
87
actor, err := s.getActorByHandle(ctx, req.Repo)
89
88
if err != nil {
90
90
-
helpers.InputError(w, to.StringPtr("RepoNotFound"))
89
89
+
helpers.InputError(w, new("RepoNotFound"))
91
90
return
92
91
}
93
92
did = actor.Did
···
124
123
125
124
var newcursor *string
126
125
if len(records) == limit {
127
127
-
newcursor = to.StringPtr(records[len(records)-1].CreatedAt)
126
126
+
newcursor = new(records[len(records)-1].CreatedAt)
128
127
}
129
128
130
129
s.writeJSON(w, 200, ComAtprotoRepoListRecordsResponse{
+1
-2
server/handle_server_confirm_email.go
···
5
5
"net/http"
6
6
"time"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
9
8
"pkg.rbrt.fr/vow/internal/helpers"
10
9
"pkg.rbrt.fr/vow/models"
11
10
)
···
39
38
}
40
39
41
40
if *urepo.EmailVerificationCode != req.Token {
42
42
-
helpers.InputError(w, to.StringPtr("InvalidToken"))
41
41
+
helpers.InputError(w, new("InvalidToken"))
43
42
return
44
43
}
45
44
+15
-16
server/handle_server_create_account.go
···
9
9
"strings"
10
10
"time"
11
11
12
12
-
"github.com/Azure/go-autorest/autorest/to"
13
12
"github.com/bluesky-social/indigo/api/atproto"
14
13
"github.com/bluesky-social/indigo/atproto/atcrypto"
15
14
atp "github.com/bluesky-social/indigo/atproto/repo"
···
59
58
var verr ValidationError
60
59
if errors.As(err, &verr) {
61
60
if verr.Field == "Email" {
62
62
-
helpers.InputError(w, to.StringPtr("InvalidEmail"))
61
61
+
helpers.InputError(w, new("InvalidEmail"))
63
62
return
64
63
}
65
64
66
65
if verr.Field == "Handle" {
67
67
-
helpers.InputError(w, to.StringPtr("InvalidHandle"))
66
66
+
helpers.InputError(w, new("InvalidHandle"))
68
67
return
69
68
}
70
69
71
70
if verr.Field == "Password" {
72
72
-
helpers.InputError(w, to.StringPtr("InvalidPassword"))
71
71
+
helpers.InputError(w, new("InvalidPassword"))
73
72
return
74
73
}
75
74
76
75
if verr.Field == "InviteCode" {
77
77
-
helpers.InputError(w, to.StringPtr("InvalidInviteCode"))
76
76
+
helpers.InputError(w, new("InvalidInviteCode"))
78
77
return
79
78
}
80
79
}
···
86
85
87
86
token := strings.TrimSpace(strings.Replace(r.Header.Get("authorization"), "Bearer ", "", 1))
88
87
if token == "" {
89
89
-
helpers.UnauthorizedError(w, to.StringPtr("must authenticate to use an existing did"))
88
88
+
helpers.UnauthorizedError(w, new("must authenticate to use an existing did"))
90
89
return
91
90
}
92
91
authDid, err := s.validateServiceAuth(r.Context(), token, "com.atproto.server.createAccount")
93
92
94
93
if err != nil {
95
94
logger.Warn("error validating authorization token", "endpoint", "com.atproto.server.createAccount", "error", err)
96
96
-
helpers.UnauthorizedError(w, to.StringPtr("invalid authorization token"))
95
95
+
helpers.UnauthorizedError(w, new("invalid authorization token"))
97
96
return
98
97
}
99
98
100
99
if authDid != signupDid {
101
101
-
helpers.ForbiddenError(w, to.StringPtr("auth did did not match signup did"))
100
100
+
helpers.ForbiddenError(w, new("auth did did not match signup did"))
102
101
return
103
102
}
104
103
}
···
111
110
return
112
111
}
113
112
if err == nil && actor.Did != signupDid {
114
114
-
helpers.InputError(w, to.StringPtr("HandleNotAvailable"))
113
113
+
helpers.InputError(w, new("HandleNotAvailable"))
115
114
return
116
115
}
117
116
118
117
if did, err := s.passport.ResolveHandle(r.Context(), request.Handle); err == nil && did != signupDid {
119
119
-
helpers.InputError(w, to.StringPtr("HandleNotAvailable"))
118
118
+
helpers.InputError(w, new("HandleNotAvailable"))
120
119
return
121
120
}
122
121
123
122
var ic models.InviteCode
124
123
if s.config.RequireInvite {
125
124
if strings.TrimSpace(request.InviteCode) == "" {
126
126
-
helpers.InputError(w, to.StringPtr("InvalidInviteCode"))
125
125
+
helpers.InputError(w, new("InvalidInviteCode"))
127
126
return
128
127
}
129
128
130
129
if err := s.db.Raw(ctx, "SELECT * FROM invite_codes WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
131
130
if err == gorm.ErrRecordNotFound {
132
132
-
helpers.InputError(w, to.StringPtr("InvalidInviteCode"))
131
131
+
helpers.InputError(w, new("InvalidInviteCode"))
133
132
return
134
133
}
135
134
logger.Error("error getting invite code from db", "error", err)
···
138
137
}
139
138
140
139
if ic.RemainingUseCount < 1 {
141
141
-
helpers.InputError(w, to.StringPtr("InvalidInviteCode"))
140
140
+
helpers.InputError(w, new("InvalidInviteCode"))
142
141
return
143
142
}
144
143
}
···
151
150
return
152
151
}
153
152
if err == nil && existingRepo.Did != signupDid {
154
154
-
helpers.InputError(w, to.StringPtr("EmailNotAvailable"))
153
153
+
helpers.InputError(w, new("EmailNotAvailable"))
155
154
return
156
155
}
157
156
···
215
214
Did: signupDid,
216
215
CreatedAt: time.Now(),
217
216
Email: request.Email,
218
218
-
EmailVerificationCode: to.StringPtr(fmt.Sprintf("%s-%s", helpers.RandomVarchar(6), helpers.RandomVarchar(6))),
217
217
+
EmailVerificationCode: new(fmt.Sprintf("%s-%s", helpers.RandomVarchar(6), helpers.RandomVarchar(6))),
219
218
Password: string(hashed),
220
219
SigningKey: k.Bytes(),
221
220
}
···
272
271
if err := s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{
273
272
RepoIdentity: &atproto.SyncSubscribeRepos_Identity{
274
273
Did: urepo.Did,
275
275
-
Handle: to.StringPtr(request.Handle),
274
274
+
Handle: new(request.Handle),
276
275
Seq: time.Now().UnixMicro(), // TODO: no
277
276
Time: time.Now().Format(util.ISO8601),
278
277
},
+2
-3
server/handle_server_create_invite_codes.go
···
4
4
"encoding/json"
5
5
"net/http"
6
6
7
7
-
"github.com/Azure/go-autorest/autorest/to"
8
7
"github.com/google/uuid"
9
8
"pkg.rbrt.fr/vow/internal/helpers"
10
9
"pkg.rbrt.fr/vow/models"
···
41
40
}
42
41
43
42
if req.CodeCount == nil {
44
44
-
req.CodeCount = to.IntPtr(1)
43
43
+
req.CodeCount = new(1)
45
44
}
46
45
47
46
if req.ForAccounts == nil {
48
48
-
req.ForAccounts = to.StringSlicePtr([]string{"admin"})
47
47
+
req.ForAccounts = new([]string{"admin"})
49
48
}
50
49
51
50
codes := make([]ComAtprotoServerCreateInviteCodesItem, 0, len(*req.ForAccounts))
+8
-9
server/handle_server_create_session.go
···
9
9
"strings"
10
10
"time"
11
11
12
12
-
"github.com/Azure/go-autorest/autorest/to"
13
12
"github.com/bluesky-social/indigo/atproto/syntax"
13
13
+
"golang.org/x/crypto/bcrypt"
14
14
+
"gorm.io/gorm"
14
15
"pkg.rbrt.fr/vow/internal/helpers"
15
16
"pkg.rbrt.fr/vow/models"
16
16
-
"golang.org/x/crypto/bcrypt"
17
17
-
"gorm.io/gorm"
18
17
)
19
18
20
19
type ComAtprotoServerCreateSessionRequest struct {
···
50
49
var verr ValidationError
51
50
if errors.As(err, &verr) {
52
51
if verr.Field == "Identifier" {
53
53
-
helpers.InputError(w, to.StringPtr("InvalidRequest"))
52
52
+
helpers.InputError(w, new("InvalidRequest"))
54
53
return
55
54
}
56
55
57
56
if verr.Field == "Password" {
58
58
-
helpers.InputError(w, to.StringPtr("InvalidRequest"))
57
57
+
helpers.InputError(w, new("InvalidRequest"))
59
58
return
60
59
}
61
60
}
···
84
83
85
84
if err != nil {
86
85
if err == gorm.ErrRecordNotFound {
87
87
-
helpers.InputError(w, to.StringPtr("InvalidRequest"))
86
86
+
helpers.InputError(w, new("InvalidRequest"))
88
87
return
89
88
}
90
89
···
97
96
if err != bcrypt.ErrMismatchedHashAndPassword {
98
97
logger.Error("error comparing hash and password", "error", err)
99
98
}
100
100
-
helpers.InputError(w, to.StringPtr("InvalidRequest"))
99
99
+
helpers.InputError(w, new("InvalidRequest"))
101
100
return
102
101
}
103
102
···
110
109
return
111
110
}
112
111
113
113
-
helpers.InputError(w, to.StringPtr("AuthFactorTokenRequired"))
112
112
+
helpers.InputError(w, new("AuthFactorTokenRequired"))
114
113
return
115
114
}
116
115
···
124
123
return
125
124
}
126
125
127
127
-
helpers.InputError(w, to.StringPtr("AuthFactorTokenRequired"))
126
126
+
helpers.InputError(w, new("AuthFactorTokenRequired"))
128
127
return
129
128
}
130
129
+1
-2
server/handle_server_deactivate_account.go
···
5
5
"net/http"
6
6
"time"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
9
8
"github.com/bluesky-social/indigo/api/atproto"
10
9
"github.com/bluesky-social/indigo/events"
11
10
"github.com/bluesky-social/indigo/util"
···
34
33
RepoAccount: &atproto.SyncSubscribeRepos_Account{
35
34
Active: false,
36
35
Did: urepo.Repo.Did,
37
37
-
Status: to.StringPtr("deactivated"),
36
36
+
Status: new("deactivated"),
38
37
Seq: time.Now().UnixMicro(), // TODO: bad puppy
39
38
Time: time.Now().Format(util.ISO8601),
40
39
},
+1
-2
server/handle_server_delete_account.go
···
6
6
"net/http"
7
7
"time"
8
8
9
9
-
"github.com/Azure/go-autorest/autorest/to"
10
9
"github.com/bluesky-social/indigo/api/atproto"
11
10
"github.com/bluesky-social/indigo/events"
12
11
"github.com/bluesky-social/indigo/util"
···
159
158
RepoAccount: &atproto.SyncSubscribeRepos_Account{
160
159
Active: false,
161
160
Did: req.Did,
162
162
-
Status: to.StringPtr("deleted"),
161
161
+
Status: new("deleted"),
163
162
Seq: time.Now().UnixMicro(),
164
163
Time: time.Now().Format(util.ISO8601),
165
164
},
+3
-4
server/handle_server_get_service_auth.go
···
10
10
"strings"
11
11
"time"
12
12
13
13
-
"github.com/Azure/go-autorest/autorest/to"
14
13
"github.com/google/uuid"
14
14
+
secp256k1secec "gitlab.com/yawning/secp256k1-voi/secec"
15
15
"pkg.rbrt.fr/vow/internal/helpers"
16
16
"pkg.rbrt.fr/vow/models"
17
17
-
secp256k1secec "gitlab.com/yawning/secp256k1-voi/secec"
18
17
)
19
18
20
19
type ServerGetServiceAuthRequest struct {
···
49
48
}
50
49
51
50
if req.Lxm == "com.atproto.server.getServiceAuth" {
52
52
-
helpers.InputError(w, to.StringPtr("may not generate auth tokens recursively"))
51
51
+
helpers.InputError(w, new("may not generate auth tokens recursively"))
53
52
return
54
53
}
55
54
···
60
59
maxExp = now + 60
61
60
}
62
61
if exp > maxExp {
63
63
-
helpers.InputError(w, to.StringPtr("expiration too big. smoller please"))
62
62
+
helpers.InputError(w, new("expiration too big. smoller please"))
64
63
return
65
64
}
66
65
+1
-2
server/handle_server_request_email_confirmation.go
···
5
5
"net/http"
6
6
"time"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
9
8
"pkg.rbrt.fr/vow/internal/helpers"
10
9
"pkg.rbrt.fr/vow/models"
11
10
)
···
17
16
urepo, _ := getContextValue[*models.RepoActor](r, contextKeyRepo)
18
17
19
18
if urepo.EmailConfirmedAt != nil {
20
20
-
helpers.InputError(w, to.StringPtr("InvalidRequest"))
19
19
+
helpers.InputError(w, new("InvalidRequest"))
21
20
return
22
21
}
23
22
+2
-3
server/handle_server_reset_password.go
···
5
5
"net/http"
6
6
"time"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
8
8
+
"golang.org/x/crypto/bcrypt"
9
9
"pkg.rbrt.fr/vow/internal/helpers"
10
10
"pkg.rbrt.fr/vow/models"
11
11
-
"golang.org/x/crypto/bcrypt"
12
11
)
13
12
14
13
type ComAtprotoServerResetPasswordRequest struct {
···
35
34
}
36
35
37
36
if urepo.PasswordResetCode == nil || urepo.PasswordResetCodeExpiresAt == nil {
38
38
-
helpers.InputError(w, to.StringPtr("InvalidToken"))
37
37
+
helpers.InputError(w, new("InvalidToken"))
39
38
return
40
39
}
41
40
+3
-4
server/handle_server_resolve_handle.go
···
2
2
3
3
import (
4
4
"context"
5
5
-
"pkg.rbrt.fr/vow/identity"
6
5
"net/http"
6
6
+
"pkg.rbrt.fr/vow/identity"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
9
8
"github.com/bluesky-social/indigo/atproto/syntax"
10
9
"pkg.rbrt.fr/vow/internal/helpers"
11
10
)
···
20
19
handle := r.URL.Query().Get("handle")
21
20
22
21
if handle == "" {
23
23
-
helpers.InputError(w, to.StringPtr("Handle must be supplied in request."))
22
22
+
helpers.InputError(w, new("Handle must be supplied in request."))
24
23
return
25
24
}
26
25
27
26
parsed, err := syntax.ParseHandle(handle)
28
27
if err != nil {
29
29
-
helpers.InputError(w, to.StringPtr("Invalid handle."))
28
28
+
helpers.InputError(w, new("Invalid handle."))
30
29
return
31
30
}
32
31
+2
-3
server/handle_sync_get_blob.go
···
6
6
"io"
7
7
"net/http"
8
8
9
9
-
"github.com/Azure/go-autorest/autorest/to"
9
9
+
"github.com/ipfs/go-cid"
10
10
"pkg.rbrt.fr/vow/internal/helpers"
11
11
"pkg.rbrt.fr/vow/models"
12
12
-
"github.com/ipfs/go-cid"
13
12
)
14
13
15
14
func (s *Server) handleSyncGetBlob(w http.ResponseWriter, r *http.Request) {
···
44
43
status := urepo.Status()
45
44
if status != nil {
46
45
if *status == "deactivated" {
47
47
-
helpers.InputError(w, to.StringPtr("RepoDeactivated"))
46
46
+
helpers.InputError(w, new("RepoDeactivated"))
48
47
return
49
48
}
50
49
}
+2
-3
server/handle_sync_list_blobs.go
···
4
4
"net/http"
5
5
"strconv"
6
6
7
7
-
"github.com/Azure/go-autorest/autorest/to"
7
7
+
"github.com/ipfs/go-cid"
8
8
"pkg.rbrt.fr/vow/internal/helpers"
9
9
"pkg.rbrt.fr/vow/models"
10
10
-
"github.com/ipfs/go-cid"
11
10
)
12
11
13
12
type ComAtprotoSyncListBlobsResponse struct {
···
54
53
status := urepo.Status()
55
54
if status != nil {
56
55
if *status == "deactivated" {
57
57
-
helpers.InputError(w, to.StringPtr("RepoDeactivated"))
56
56
+
helpers.InputError(w, new("RepoDeactivated"))
58
57
return
59
58
}
60
59
}
+2
-3
server/handle_well_known.go
···
5
5
"net/http"
6
6
"strings"
7
7
8
8
-
"github.com/Azure/go-autorest/autorest/to"
9
8
"gorm.io/gorm"
10
9
"pkg.rbrt.fr/vow/internal/helpers"
11
10
)
···
72
71
73
72
host := r.Host
74
73
if host == "" {
75
75
-
helpers.InputError(w, to.StringPtr("Invalid handle."))
74
74
+
helpers.InputError(w, new("Invalid handle."))
76
75
return
77
76
}
78
77
···
123
122
Issuer: "https://" + s.config.Hostname,
124
123
RequestParameterSupported: true,
125
124
RequestUriParameterSupported: true,
126
126
-
RequireRequestUriRegistration: to.BoolPtr(true),
125
125
+
RequireRequestUriRegistration: new(true),
127
126
ScopesSupported: VowSupportedScopes,
128
127
SubjectTypesSupported: []string{"public"},
129
128
ResponseTypesSupported: []string{"code"},
+6
-7
server/middleware.go
···
10
10
"strings"
11
11
"time"
12
12
13
13
-
"github.com/Azure/go-autorest/autorest/to"
14
13
"github.com/golang-jwt/jwt/v4"
14
14
+
"gitlab.com/yawning/secp256k1-voi"
15
15
+
secp256k1secec "gitlab.com/yawning/secp256k1-voi/secec"
16
16
+
"gorm.io/gorm"
15
17
"pkg.rbrt.fr/vow/internal/helpers"
16
18
"pkg.rbrt.fr/vow/models"
17
19
"pkg.rbrt.fr/vow/oauth/dpop"
18
20
"pkg.rbrt.fr/vow/oauth/provider"
19
19
-
"gitlab.com/yawning/secp256k1-voi"
20
20
-
secp256k1secec "gitlab.com/yawning/secp256k1-voi/secec"
21
21
-
"gorm.io/gorm"
22
21
)
23
22
24
23
// context keys for values set by middleware
···
48
47
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
49
48
username, password, ok := r.BasicAuth()
50
49
if !ok || username != "admin" || password != s.config.AdminPassword {
51
51
-
helpers.InputError(w, to.StringPtr("Unauthorized"))
50
50
+
helpers.InputError(w, new("Unauthorized"))
52
51
return
53
52
}
54
53
next.ServeHTTP(w, r)
···
293
292
w.Header().Add("access-control-expose-headers", "DPoP-Nonce")
294
293
}
295
294
296
296
-
proof, err := s.oauthProvider.DpopManager.CheckProof(r.Method, "https://"+s.config.Hostname+r.URL.String(), r.Header, to.StringPtr(accessToken))
295
295
+
proof, err := s.oauthProvider.DpopManager.CheckProof(r.Method, "https://"+s.config.Hostname+r.URL.String(), r.Header, new(accessToken))
297
296
if err != nil {
298
297
if errors.Is(err, dpop.ErrUseDpopNonce) {
299
298
w.Header().Set("WWW-Authenticate", `DPoP error="use_dpop_nonce"`)
···
322
321
323
322
if *oauthToken.Parameters.DpopJkt != proof.JKT {
324
323
logger.Error("jkt mismatch", "token", oauthToken.Parameters.DpopJkt, "proof", proof.JKT)
325
325
-
helpers.InputError(w, to.StringPtr("dpop jkt mismatch"))
324
324
+
helpers.InputError(w, new("dpop jkt mismatch"))
326
325
return
327
326
}
328
327
+11
-12
server/repo.go
···
9
9
"sync"
10
10
"time"
11
11
12
12
-
"github.com/Azure/go-autorest/autorest/to"
13
12
"github.com/bluesky-social/indigo/api/atproto"
14
13
"github.com/bluesky-social/indigo/atproto/atcrypto"
15
14
"github.com/bluesky-social/indigo/atproto/atdata"
···
264
263
}
265
264
} else if op.Rkey == nil {
266
265
// creates that don't supply an rkey will have one generated for them
267
267
-
op.Rkey = to.StringPtr(rm.clock.Next().String())
266
266
+
op.Rkey = new(rm.clock.Next().String())
268
267
writes[i].Rkey = op.Rkey
269
268
}
270
269
···
320
319
})
321
320
322
321
results = append(results, ApplyWriteResult{
323
323
-
Type: to.StringPtr(OpTypeCreate.String()),
324
324
-
Uri: to.StringPtr("at://" + urepo.Did + "/" + op.Collection + "/" + *op.Rkey),
325
325
-
Cid: to.StringPtr(nc.String()),
326
326
-
ValidationStatus: to.StringPtr("valid"), // TODO: obviously this might not be true atm lol
322
322
+
Type: new(OpTypeCreate.String()),
323
323
+
Uri: new("at://" + urepo.Did + "/" + op.Collection + "/" + *op.Rkey),
324
324
+
Cid: new(nc.String()),
325
325
+
ValidationStatus: new("valid"), // TODO: obviously this might not be true atm lol
327
326
})
328
327
case OpTypeDelete:
329
328
// try to find the old record in the database
···
349
348
ops = append(ops, atpOp)
350
349
351
350
results = append(results, ApplyWriteResult{
352
352
-
Type: to.StringPtr(OpTypeDelete.String()),
351
351
+
Type: new(OpTypeDelete.String()),
353
352
})
354
353
case OpTypeUpdate:
355
354
// HACK: same hack as above for type fixes
···
389
388
})
390
389
391
390
results = append(results, ApplyWriteResult{
392
392
-
Type: to.StringPtr(OpTypeUpdate.String()),
393
393
-
Uri: to.StringPtr("at://" + urepo.Did + "/" + op.Collection + "/" + *op.Rkey),
394
394
-
Cid: to.StringPtr(nc.String()),
395
395
-
ValidationStatus: to.StringPtr("valid"), // TODO: obviously this might not be true atm lol
391
391
+
Type: new(OpTypeUpdate.String()),
392
392
+
Uri: new("at://" + urepo.Did + "/" + op.Collection + "/" + *op.Rkey),
393
393
+
Cid: new(nc.String()),
394
394
+
ValidationStatus: new("valid"), // TODO: obviously this might not be true atm lol
396
395
})
397
396
}
398
397
}
···
541
540
}
542
541
543
542
for i := range results {
544
544
-
results[i].Type = to.StringPtr(*results[i].Type + "Result")
543
543
+
results[i].Type = new(*results[i].Type + "Result")
545
544
results[i].Commit = &RepoCommit{
546
545
Cid: newroot.String(),
547
546
Rev: rev,