+2
-2
.air.toml
.air/appview.toml
+2
-2
.air.toml
.air/appview.toml
+7
.air/knotserver.toml
+7
.air/knotserver.toml
+40
-22
appview/auth/auth.go
+40
-22
appview/auth/auth.go
···
2
2
3
3
import (
4
4
"context"
5
-
"encoding/json"
6
5
"fmt"
7
6
"net/http"
8
7
"time"
···
44
43
return dir.Lookup(ctx, *id)
45
44
}
46
45
47
-
func (a *Auth) CreateInitialSession(ctx context.Context, username, appPassword string) (*comatproto.ServerCreateSession_Output, error) {
48
-
resolved, err := ResolveIdent(ctx, username)
49
-
if err != nil {
50
-
return nil, fmt.Errorf("invalid handle: %s", err)
51
-
}
46
+
func (a *Auth) CreateInitialSession(ctx context.Context, resolved *identity.Identity, appPassword string) (*comatproto.ServerCreateSession_Output, error) {
52
47
53
48
pdsUrl := resolved.PDSEndpoint()
54
49
client := xrpc.Client{
···
143
138
return s.Status
144
139
}
145
140
146
-
func (a *Auth) StoreSession(r *http.Request, w http.ResponseWriter, atSessionish Sessionish) error {
147
-
var didDoc identity.DIDDocument
148
-
149
-
bytes, _ := json.Marshal(atSessionish.GetDidDoc())
150
-
err := json.Unmarshal(bytes, &didDoc)
151
-
if err != nil {
152
-
return fmt.Errorf("invalid did document for session")
153
-
}
154
-
155
-
identity := identity.ParseIdentity(&didDoc)
156
-
pdsEndpoint := identity.PDSEndpoint()
157
-
158
-
if pdsEndpoint == "" {
159
-
return fmt.Errorf("no pds endpoint found")
160
-
}
161
-
141
+
func (a *Auth) StoreSession(r *http.Request, w http.ResponseWriter, atSessionish Sessionish, pdsEndpoint string) error {
162
142
clientSession, _ := a.Store.Get(r, appview.SESSION_NAME)
163
143
clientSession.Values[appview.SESSION_HANDLE] = atSessionish.GetHandle()
164
144
clientSession.Values[appview.SESSION_DID] = atSessionish.GetDid()
···
170
150
171
151
return clientSession.Save(r, w)
172
152
}
153
+
154
+
func (a *Auth) AuthorizedClient(r *http.Request) (*xrpc.Client, error) {
155
+
clientSession, err := a.Store.Get(r, "appview-session")
156
+
157
+
if err != nil || clientSession.IsNew {
158
+
return nil, err
159
+
}
160
+
161
+
did := clientSession.Values["did"].(string)
162
+
pdsUrl := clientSession.Values["pds"].(string)
163
+
accessJwt := clientSession.Values["accessJwt"].(string)
164
+
refreshJwt := clientSession.Values["refreshJwt"].(string)
165
+
166
+
client := &xrpc.Client{
167
+
Host: pdsUrl,
168
+
Auth: &xrpc.AuthInfo{
169
+
AccessJwt: accessJwt,
170
+
RefreshJwt: refreshJwt,
171
+
Did: did,
172
+
},
173
+
}
174
+
175
+
return client, nil
176
+
}
177
+
178
+
func (a *Auth) GetSession(r *http.Request) (*sessions.Session, error) {
179
+
return a.Store.Get(r, appview.SESSION_NAME)
180
+
}
181
+
182
+
func (a *Auth) GetDID(r *http.Request) string {
183
+
clientSession, _ := a.Store.Get(r, appview.SESSION_NAME)
184
+
return clientSession.Values[appview.SESSION_DID].(string)
185
+
}
186
+
187
+
func (a *Auth) GetHandle(r *http.Request) string {
188
+
clientSession, _ := a.Store.Get(r, appview.SESSION_NAME)
189
+
return clientSession.Values[appview.SESSION_HANDLE].(string)
190
+
}
+9
-5
appview/db/db.go
+9
-5
appview/db/db.go
···
6
6
"log"
7
7
8
8
"github.com/google/uuid"
9
+
9
10
_ "github.com/mattn/go-sqlite3"
10
11
)
11
12
···
25
26
did text not null,
26
27
secret text not null,
27
28
created integer default (strftime('%s', 'now')),
28
-
registered integer
29
+
registered integer);
30
+
create table if not exists public_keys (
31
+
id integer primary key autoincrement,
32
+
did text not null,
33
+
name text not null,
34
+
key text not null,
35
+
created timestamp default current_timestamp,
36
+
unique(did, name, key)
29
37
);
30
38
`)
31
39
if err != nil {
···
85
93
}
86
94
87
95
secret := uuid.New().String()
88
-
89
-
if err != nil {
90
-
return "", err
91
-
}
92
96
93
97
_, err = d.db.Exec(`
94
98
insert into registrations (domain, did, secret)
+70
appview/db/pubkeys.go
+70
appview/db/pubkeys.go
···
1
+
package db
2
+
3
+
import "time"
4
+
5
+
func (d *DB) AddPublicKey(did, name, key string) error {
6
+
query := `insert into public_keys (did, name, key) values (?, ?, ?)`
7
+
_, err := d.db.Exec(query, did, name, key)
8
+
return err
9
+
}
10
+
11
+
func (d *DB) RemovePublicKey(did string) error {
12
+
query := `delete from public_keys where did = ?`
13
+
_, err := d.db.Exec(query, did)
14
+
return err
15
+
}
16
+
17
+
type PublicKey struct {
18
+
Key string
19
+
Name string
20
+
DID string
21
+
Created time.Time
22
+
}
23
+
24
+
func (d *DB) GetAllPublicKeys() ([]PublicKey, error) {
25
+
var keys []PublicKey
26
+
27
+
rows, err := d.db.Query(`select key, name, did, created from public_keys`)
28
+
if err != nil {
29
+
return nil, err
30
+
}
31
+
defer rows.Close()
32
+
33
+
for rows.Next() {
34
+
var publicKey PublicKey
35
+
if err := rows.Scan(&publicKey.Key, &publicKey.Name, &publicKey.DID, &publicKey.Created); err != nil {
36
+
return nil, err
37
+
}
38
+
keys = append(keys, publicKey)
39
+
}
40
+
41
+
if err := rows.Err(); err != nil {
42
+
return nil, err
43
+
}
44
+
45
+
return keys, nil
46
+
}
47
+
48
+
func (d *DB) GetPublicKeys(did string) ([]PublicKey, error) {
49
+
var keys []PublicKey
50
+
51
+
rows, err := d.db.Query(`select did, key, name, created from public_keys where did = ?`, did)
52
+
if err != nil {
53
+
return nil, err
54
+
}
55
+
defer rows.Close()
56
+
57
+
for rows.Next() {
58
+
var publicKey PublicKey
59
+
if err := rows.Scan(&publicKey.DID, &publicKey.Key, &publicKey.Name, &publicKey.Created); err != nil {
60
+
return nil, err
61
+
}
62
+
keys = append(keys, publicKey)
63
+
}
64
+
65
+
if err := rows.Err(); err != nil {
66
+
return nil, err
67
+
}
68
+
69
+
return keys, nil
70
+
}
+2
-2
appview/state/middleware.go
+2
-2
appview/state/middleware.go
···
16
16
func AuthMiddleware(s *State) Middleware {
17
17
return func(next http.Handler) http.Handler {
18
18
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
19
-
session, _ := s.Auth.Store.Get(r, appview.SESSION_NAME)
19
+
session, _ := s.auth.Store.Get(r, appview.SESSION_NAME)
20
20
authorized, ok := session.Values[appview.SESSION_AUTHENTICATED].(bool)
21
21
22
22
if !ok || !authorized {
···
56
56
57
57
sessionish := auth.RefreshSessionWrapper{atSession}
58
58
59
-
err = s.Auth.StoreSession(r, w, &sessionish)
59
+
err = s.auth.StoreSession(r, w, &sessionish, pdsUrl)
60
60
if err != nil {
61
61
log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err)
62
62
return
+79
-15
appview/state/state.go
+79
-15
appview/state/state.go
···
9
9
"net/http"
10
10
"time"
11
11
12
+
comatproto "github.com/bluesky-social/indigo/api/atproto"
13
+
lexutil "github.com/bluesky-social/indigo/lex/util"
14
+
"github.com/gliderlabs/ssh"
12
15
"github.com/go-chi/chi/v5"
16
+
"github.com/google/uuid"
17
+
shbild "github.com/icyphox/bild/api/bild"
13
18
"github.com/icyphox/bild/appview"
14
19
"github.com/icyphox/bild/appview/auth"
15
20
"github.com/icyphox/bild/appview/db"
16
21
)
17
22
18
23
type State struct {
19
-
Db *db.DB
20
-
Auth *auth.Auth
24
+
db *db.DB
25
+
auth *auth.Auth
21
26
}
22
27
23
28
func Make() (*State, error) {
···
42
47
log.Println("unimplemented")
43
48
return
44
49
case http.MethodPost:
45
-
username := r.FormValue("username")
46
-
appPassword := r.FormValue("password")
50
+
handle := r.FormValue("handle")
51
+
appPassword := r.FormValue("app_password")
47
52
48
-
atSession, err := s.Auth.CreateInitialSession(ctx, username, appPassword)
53
+
fmt.Println("handle", handle)
54
+
fmt.Println("app_password", appPassword)
55
+
56
+
resolved, err := auth.ResolveIdent(ctx, handle)
57
+
if err != nil {
58
+
log.Printf("resolving identity: %s", err)
59
+
return
60
+
}
61
+
62
+
atSession, err := s.auth.CreateInitialSession(ctx, resolved, appPassword)
49
63
if err != nil {
50
64
log.Printf("creating initial session: %s", err)
51
65
return
52
66
}
53
-
sessionish := auth.CreateSessionWrapper{atSession}
67
+
sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession}
54
68
55
-
err = s.Auth.StoreSession(r, w, &sessionish)
69
+
err = s.auth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint())
56
70
if err != nil {
57
71
log.Printf("storing session: %s", err)
58
72
return
···
65
79
}
66
80
67
81
// requires auth
68
-
func (s *State) Key(w http.ResponseWriter, r *http.Request) {
82
+
func (s *State) RegistrationKey(w http.ResponseWriter, r *http.Request) {
69
83
switch r.Method {
70
84
case http.MethodGet:
71
85
// list open registrations under this did
72
86
73
87
return
74
88
case http.MethodPost:
75
-
session, err := s.Auth.Store.Get(r, appview.SESSION_NAME)
89
+
session, err := s.auth.Store.Get(r, appview.SESSION_NAME)
76
90
if err != nil || session.IsNew {
77
91
log.Println("unauthorized attempt to generate registration key")
78
92
http.Error(w, "Forbidden", http.StatusUnauthorized)
···
83
97
84
98
// check if domain is valid url, and strip extra bits down to just host
85
99
domain := r.FormValue("domain")
86
-
if domain == "" || err != nil {
87
-
log.Println(err)
100
+
if domain == "" {
88
101
http.Error(w, "Invalid form", http.StatusBadRequest)
89
102
return
90
103
}
91
104
92
-
key, err := s.Db.GenerateRegistrationKey(domain, did)
105
+
key, err := s.db.GenerateRegistrationKey(domain, did)
93
106
94
107
if err != nil {
95
108
log.Println(err)
···
98
111
}
99
112
100
113
w.Write([]byte(key))
114
+
}
115
+
}
116
+
117
+
func (s *State) Keys(w http.ResponseWriter, r *http.Request) {
118
+
switch r.Method {
119
+
case http.MethodGet:
120
+
w.Write([]byte("unimplemented"))
121
+
log.Println("unimplemented")
122
+
return
123
+
case http.MethodPut:
124
+
did := s.auth.GetDID(r)
125
+
key := r.FormValue("key")
126
+
name := r.FormValue("name")
127
+
client, _ := s.auth.AuthorizedClient(r)
128
+
129
+
_, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key))
130
+
if err != nil {
131
+
log.Printf("parsing public key: %s", err)
132
+
return
133
+
}
134
+
135
+
if err := s.db.AddPublicKey(did, name, key); err != nil {
136
+
log.Printf("adding public key: %s", err)
137
+
return
138
+
}
139
+
140
+
// store in pds too
141
+
resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
142
+
Collection: "sh.bild.publicKey",
143
+
Repo: did,
144
+
Rkey: uuid.New().String(),
145
+
Record: &lexutil.LexiconTypeDecoder{Val: &shbild.PublicKey{
146
+
Created: time.Now().String(),
147
+
Key: key,
148
+
Name: name,
149
+
}},
150
+
})
151
+
152
+
// invalid record
153
+
if err != nil {
154
+
log.Printf("failed to create record: %s", err)
155
+
return
156
+
}
157
+
158
+
log.Println("created atproto record: ", resp.Uri)
159
+
101
160
return
102
161
}
103
162
}
···
114
173
115
174
log.Println("checking ", domain)
116
175
117
-
secret, err := s.Db.GetRegistrationKey(domain)
176
+
secret, err := s.db.GetRegistrationKey(domain)
118
177
if err != nil {
119
178
log.Printf("no key found for domain %s: %s\n", domain, err)
120
179
return
···
164
223
w.Write([]byte("check success"))
165
224
166
225
// mark as registered
167
-
err = s.Db.Register(domain)
226
+
err = s.db.Register(domain)
168
227
if err != nil {
169
228
log.Println("failed to register domain", err)
170
229
http.Error(w, err.Error(), http.StatusInternalServerError)
···
201
260
202
261
r.Group(func(r chi.Router) {
203
262
r.Use(AuthMiddleware(s))
204
-
r.Post("/key", s.Key)
263
+
r.Post("/key", s.RegistrationKey)
205
264
})
265
+
})
266
+
267
+
r.Route("/settings", func(r chi.Router) {
268
+
r.Use(AuthMiddleware(s))
269
+
r.Put("/keys", s.Keys)
206
270
})
207
271
208
272
return r
+1
-5
git/git.go
+1
-5
git/git.go
···
202
202
}
203
203
204
204
func (g *GitRepo) FindMainBranch(branches []string) (string, error) {
205
-
branches = append(branches, []string{
206
-
"main",
207
-
"master",
208
-
"trunk",
209
-
}...)
205
+
210
206
for _, b := range branches {
211
207
_, err := g.r.ResolveRevision(plumbing.Revision(b))
212
208
if err == nil {