From 492f7060ba54516a37428581d23c24b5039d3975 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Thu, 8 May 2025 11:00:59 +0300 Subject: [PATCH] appview: swap out old auth service for oauth Also does some driveby config refactoring. --- .air/appview.toml | 2 +- appview/config.go | 47 ++++- appview/consts.go | 3 + appview/db/db.go | 26 +++ appview/db/oauth.go | 173 ++++++++++++++++ appview/middleware/middleware.go | 63 +----- appview/oauth/oauth.go | 2 + appview/pages/pages.go | 78 +++---- appview/pages/templates/user/oauthlogin.html | 71 +++++++ appview/settings/settings.go | 45 ++-- appview/state/artifact.go | 29 ++- appview/state/follow.go | 12 +- appview/state/git_http.go | 4 +- appview/state/middleware.go | 4 +- appview/state/profile.go | 29 +-- appview/state/pull.go | 127 +++++++----- appview/state/repo.go | 158 ++++++++------ appview/state/repo_util.go | 6 +- appview/state/router.go | 53 +++-- appview/state/star.go | 12 +- appview/state/state.go | 205 ++++++++++--------- appview/tid.go | 2 +- cmd/appview/main.go | 4 +- 23 files changed, 765 insertions(+), 390 deletions(-) create mode 100644 appview/db/oauth.go create mode 100644 appview/pages/templates/user/oauthlogin.html diff --git a/.air/appview.toml b/.air/appview.toml index 01efc93c..877f17ce 100644 --- a/.air/appview.toml +++ b/.air/appview.toml @@ -1,6 +1,6 @@ [build] cmd = "tailwindcss -i input.css -o ./appview/pages/static/tw.css && go build -o .bin/app ./cmd/appview/main.go" -bin = ".bin/app" +bin = ";set -o allexport && source .env && set +o allexport; .bin/app" root = "." exclude_regex = [".*_templ.go"] diff --git a/appview/config.go b/appview/config.go index c7d321ca..7d82b281 100644 --- a/appview/config.go +++ b/appview/config.go @@ -6,17 +6,44 @@ import ( "github.com/sethvargo/go-envconfig" ) +type CoreConfig struct { + CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"` + DbPath string `env:"DB_PATH, default=appview.db"` + ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"` + AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"` + Dev bool `env:"DEV, default=false"` +} + +type OAuthConfig struct { + Jwks string `env:"JWKS"` + ServerMetadataUrl string `env:"SERVER_METADATA_URL"` +} + +type JetstreamConfig struct { + Endpoint string `env:"ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` +} + +type ResendConfig struct { + ApiKey string `env:"API_KEY"` +} + +type CamoConfig struct { + Host string `env:"HOST, default=https://camo.tangled.sh"` + SharedSecret string `env:"SHARED_SECRET"` +} + +type AvatarConfig struct { + Host string `env:"HOST, default=https://avatar.tangled.sh"` + SharedSecret string `env:"SHARED_SECRET"` +} + type Config struct { - CookieSecret string `env:"TANGLED_COOKIE_SECRET, default=00000000000000000000000000000000"` - DbPath string `env:"TANGLED_DB_PATH, default=appview.db"` - ListenAddr string `env:"TANGLED_LISTEN_ADDR, default=0.0.0.0:3000"` - Dev bool `env:"TANGLED_DEV, default=false"` - JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` - ResendApiKey string `env:"TANGLED_RESEND_API_KEY"` - CamoHost string `env:"TANGLED_CAMO_HOST, default=https://camo.tangled.sh"` - CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"` - AvatarSharedSecret string `env:"TANGLED_AVATAR_SHARED_SECRET"` - AvatarHost string `env:"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh"` + Core CoreConfig `env:",prefix=TANGLED_"` + Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` + Resend ResendConfig `env:",prefix=TANGLED_RESEND_"` + Camo CamoConfig `env:",prefix=TANGLED_CAMO_"` + Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"` + OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"` } func LoadConfig(ctx context.Context) (*Config, error) { diff --git a/appview/consts.go b/appview/consts.go index 3a15eb1b..ea86179b 100644 --- a/appview/consts.go +++ b/appview/consts.go @@ -9,4 +9,7 @@ const ( SessionRefreshJwt = "refreshJwt" SessionExpiry = "expiry" SessionAuthenticated = "authenticated" + + SessionDpopPrivateJwk = "dpopPrivateJwk" + SessionDpopAuthServerNonce = "dpopAuthServerNonce" ) diff --git a/appview/db/db.go b/appview/db/db.go index eb190533..b5339da0 100644 --- a/appview/db/db.go +++ b/appview/db/db.go @@ -288,6 +288,32 @@ func Make(dbPath string) (*DB, error) { foreign key (at_uri) references repos(at_uri) on delete cascade ); + create table if not exists oauth_requests ( + id integer primary key autoincrement, + auth_server_iss text not null, + state text not null, + did text not null, + handle text not null, + pds_url text not null, + pkce_verifier text not null, + dpop_auth_server_nonce text not null, + dpop_private_jwk text not null + ); + + create table if not exists oauth_sessions ( + id integer primary key autoincrement, + did text not null, + handle text not null, + pds_url text not null, + auth_server_iss text not null, + access_jwt text not null, + refresh_jwt text not null, + dpop_pds_nonce text, + dpop_auth_server_nonce text not null, + dpop_private_jwk text not null, + expiry text not null + ); + create table if not exists migrations ( id integer primary key autoincrement, name text unique diff --git a/appview/db/oauth.go b/appview/db/oauth.go new file mode 100644 index 00000000..684f9090 --- /dev/null +++ b/appview/db/oauth.go @@ -0,0 +1,173 @@ +package db + +type OAuthRequest struct { + ID uint + AuthserverIss string + Handle string + State string + Did string + PdsUrl string + PkceVerifier string + DpopAuthserverNonce string + DpopPrivateJwk string +} + +func SaveOAuthRequest(e Execer, oauthRequest OAuthRequest) error { + _, err := e.Exec(` + insert into oauth_requests ( + auth_server_iss, + state, + handle, + did, + pds_url, + pkce_verifier, + dpop_auth_server_nonce, + dpop_private_jwk + ) values (?, ?, ?, ?, ?, ?, ?, ?)`, + oauthRequest.AuthserverIss, + oauthRequest.State, + oauthRequest.Handle, + oauthRequest.Did, + oauthRequest.PdsUrl, + oauthRequest.PkceVerifier, + oauthRequest.DpopAuthserverNonce, + oauthRequest.DpopPrivateJwk, + ) + return err +} + +func GetOAuthRequestByState(e Execer, state string) (OAuthRequest, error) { + var req OAuthRequest + err := e.QueryRow(` + select + id, + auth_server_iss, + handle, + state, + did, + pds_url, + pkce_verifier, + dpop_auth_server_nonce, + dpop_private_jwk + from oauth_requests + where state = ?`, state).Scan( + &req.ID, + &req.AuthserverIss, + &req.Handle, + &req.State, + &req.Did, + &req.PdsUrl, + &req.PkceVerifier, + &req.DpopAuthserverNonce, + &req.DpopPrivateJwk, + ) + return req, err +} + +func DeleteOAuthRequestByState(e Execer, state string) error { + _, err := e.Exec(` + delete from oauth_requests + where state = ?`, state) + return err +} + +type OAuthSession struct { + ID uint + Handle string + Did string + PdsUrl string + AccessJwt string + RefreshJwt string + AuthServerIss string + DpopPdsNonce string + DpopAuthserverNonce string + DpopPrivateJwk string + Expiry string +} + +func SaveOAuthSession(e Execer, session OAuthSession) error { + _, err := e.Exec(` + insert into oauth_sessions ( + did, + handle, + pds_url, + access_jwt, + refresh_jwt, + auth_server_iss, + dpop_auth_server_nonce, + dpop_private_jwk, + expiry + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + session.Did, + session.Handle, + session.PdsUrl, + session.AccessJwt, + session.RefreshJwt, + session.AuthServerIss, + session.DpopAuthserverNonce, + session.DpopPrivateJwk, + session.Expiry, + ) + return err +} + +func RefreshOAuthSession(e Execer, did string, accessJwt, refreshJwt, expiry string) error { + _, err := e.Exec(` + update oauth_sessions + set access_jwt = ?, refresh_jwt = ?, expiry = ? + where did = ?`, + accessJwt, + refreshJwt, + expiry, + did, + ) + return err +} + +func GetOAuthSessionByDid(e Execer, did string) (*OAuthSession, error) { + var session OAuthSession + err := e.QueryRow(` + select + id, + did, + handle, + pds_url, + access_jwt, + refresh_jwt, + auth_server_iss, + dpop_auth_server_nonce, + dpop_private_jwk, + expiry + from oauth_sessions + where did = ?`, did).Scan( + &session.ID, + &session.Did, + &session.Handle, + &session.PdsUrl, + &session.AccessJwt, + &session.RefreshJwt, + &session.AuthServerIss, + &session.DpopAuthserverNonce, + &session.DpopPrivateJwk, + &session.Expiry, + ) + return &session, err +} + +func DeleteOAuthSessionByDid(e Execer, did string) error { + _, err := e.Exec(` + delete from oauth_sessions + where did = ?`, did) + return err +} + +func UpdateDpopPdsNonce(e Execer, did string, dpopPdsNonce string) error { + _, err := e.Exec(` + update oauth_sessions + set dpop_pds_nonce = ? + where did = ?`, + dpopPdsNonce, + did, + ) + return err +} diff --git a/appview/middleware/middleware.go b/appview/middleware/middleware.go index 51aa75fd..60d90b51 100644 --- a/appview/middleware/middleware.go +++ b/appview/middleware/middleware.go @@ -5,18 +5,14 @@ import ( "log" "net/http" "strconv" - "time" - comatproto "github.com/bluesky-social/indigo/api/atproto" - "github.com/bluesky-social/indigo/xrpc" - "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pagination" ) type Middleware func(http.Handler) http.Handler -func AuthMiddleware(a *auth.Auth) Middleware { +func AuthMiddleware(a *oauth.OAuth) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { redirectFunc := func(w http.ResponseWriter, r *http.Request) { @@ -29,68 +25,19 @@ func AuthMiddleware(a *auth.Auth) Middleware { } } - session, err := a.GetSession(r) - if session.IsNew || err != nil { + _, auth, err := a.GetSession(r) + if err != nil { log.Printf("not logged in, redirecting") redirectFunc(w, r) return } - authorized, ok := session.Values[appview.SessionAuthenticated].(bool) - if !ok || !authorized { + if !auth { log.Printf("not logged in, redirecting") redirectFunc(w, r) return } - // refresh if nearing expiry - // TODO: dedup with /login - expiryStr := session.Values[appview.SessionExpiry].(string) - expiry, err := time.Parse(time.RFC3339, expiryStr) - if err != nil { - log.Println("invalid expiry time", err) - redirectFunc(w, r) - return - } - pdsUrl, ok1 := session.Values[appview.SessionPds].(string) - did, ok2 := session.Values[appview.SessionDid].(string) - refreshJwt, ok3 := session.Values[appview.SessionRefreshJwt].(string) - - if !ok1 || !ok2 || !ok3 { - log.Println("invalid expiry time", err) - redirectFunc(w, r) - return - } - - if time.Now().After(expiry) { - log.Println("token expired, refreshing ...") - - client := xrpc.Client{ - Host: pdsUrl, - Auth: &xrpc.AuthInfo{ - Did: did, - AccessJwt: refreshJwt, - RefreshJwt: refreshJwt, - }, - } - atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) - if err != nil { - log.Println("failed to refresh session", err) - redirectFunc(w, r) - return - } - - sessionish := auth.RefreshSessionWrapper{atSession} - - err = a.StoreSession(r, w, &sessionish, pdsUrl) - if err != nil { - log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err) - return - } - - log.Println("successfully refreshed token") - } - next.ServeHTTP(w, r) }) } diff --git a/appview/oauth/oauth.go b/appview/oauth/oauth.go index 07c9062f..1cdd190b 100644 --- a/appview/oauth/oauth.go +++ b/appview/oauth/oauth.go @@ -48,6 +48,8 @@ func (o *OAuth) SaveSession(w http.ResponseWriter, r *http.Request, oreq db.OAut } userSession.Values[appview.SessionDid] = oreq.Did + userSession.Values[appview.SessionHandle] = oreq.Handle + userSession.Values[appview.SessionPds] = oreq.PdsUrl userSession.Values[appview.SessionAuthenticated] = true err = userSession.Save(r, w) if err != nil { diff --git a/appview/pages/pages.go b/appview/pages/pages.go index ae18d026..58af5e77 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -16,8 +16,8 @@ import ( "strings" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages/markup" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" "tangled.sh/tangled.sh/core/appview/pagination" @@ -48,14 +48,14 @@ type Pages struct { func NewPages(config *appview.Config) *Pages { // initialized with safe defaults, can be overriden per use rctx := &markup.RenderContext{ - IsDev: config.Dev, - CamoUrl: config.CamoHost, - CamoSecret: config.CamoSharedSecret, + IsDev: config.Core.Dev, + CamoUrl: config.Camo.Host, + CamoSecret: config.Camo.SharedSecret, } p := &Pages{ t: make(map[string]*template.Template), - dev: config.Dev, + dev: config.Core.Dev, embedFS: Files, rctx: rctx, templateDir: "appview/pages", @@ -249,8 +249,12 @@ func (p *Pages) Login(w io.Writer, params LoginParams) error { return p.executePlain("user/login", w, params) } +func (p *Pages) OAuthLogin(w io.Writer, params LoginParams) error { + return p.executePlain("user/oauthlogin", w, params) +} + type TimelineParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Timeline []db.TimelineEvent DidHandleMap map[string]string } @@ -260,7 +264,7 @@ func (p *Pages) Timeline(w io.Writer, params TimelineParams) error { } type SettingsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User PubKeys []db.PublicKey Emails []db.Email } @@ -270,7 +274,7 @@ func (p *Pages) Settings(w io.Writer, params SettingsParams) error { } type KnotsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Registrations []db.Registration } @@ -279,7 +283,7 @@ func (p *Pages) Knots(w io.Writer, params KnotsParams) error { } type KnotParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string Registration *db.Registration Members []string @@ -291,7 +295,7 @@ func (p *Pages) Knot(w io.Writer, params KnotParams) error { } type NewRepoParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Knots []string } @@ -300,7 +304,7 @@ func (p *Pages) NewRepo(w io.Writer, params NewRepoParams) error { } type ForkRepoParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Knots []string RepoInfo repoinfo.RepoInfo } @@ -310,7 +314,7 @@ func (p *Pages) ForkRepo(w io.Writer, params ForkRepoParams) error { } type ProfilePageParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Repos []db.Repo CollaboratingRepos []db.Repo ProfileTimeline *db.ProfileTimeline @@ -335,7 +339,7 @@ func (p *Pages) ProfilePage(w io.Writer, params ProfilePageParams) error { } type ReposPageParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Repos []db.Repo Card ProfileCard @@ -356,7 +360,7 @@ func (p *Pages) FollowFragment(w io.Writer, params FollowFragmentParams) error { } type EditBioParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Profile *db.Profile } @@ -365,7 +369,7 @@ func (p *Pages) EditBioFragment(w io.Writer, params EditBioParams) error { } type EditPinsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Profile *db.Profile AllRepos []PinnedRepo DidHandleMap map[string]string @@ -403,7 +407,7 @@ func (p *Pages) RepoDescriptionFragment(w io.Writer, params RepoDescriptionParam } type RepoIndexParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string TagMap map[string][]string @@ -444,7 +448,7 @@ func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error { } type RepoLogParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo TagMap map[string][]string types.RepoLogResponse @@ -458,7 +462,7 @@ func (p *Pages) RepoLog(w io.Writer, params RepoLogParams) error { } type RepoCommitParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string EmailToDidOrHandle map[string]string @@ -472,7 +476,7 @@ func (p *Pages) RepoCommit(w io.Writer, params RepoCommitParams) error { } type RepoTreeParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string BreadCrumbs [][]string @@ -508,7 +512,7 @@ func (p *Pages) RepoTree(w io.Writer, params RepoTreeParams) error { } type RepoBranchesParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string types.RepoBranchesResponse @@ -520,7 +524,7 @@ func (p *Pages) RepoBranches(w io.Writer, params RepoBranchesParams) error { } type RepoTagsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string types.RepoTagsResponse @@ -534,7 +538,7 @@ func (p *Pages) RepoTags(w io.Writer, params RepoTagsParams) error { } type RepoArtifactParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Artifact db.Artifact } @@ -544,7 +548,7 @@ func (p *Pages) RepoArtifactFragment(w io.Writer, params RepoArtifactParams) err } type RepoBlobParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string BreadCrumbs [][]string @@ -606,7 +610,7 @@ type Collaborator struct { } type RepoSettingsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Collaborators []Collaborator Active string @@ -622,7 +626,7 @@ func (p *Pages) RepoSettings(w io.Writer, params RepoSettingsParams) error { } type RepoIssuesParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string Issues []db.Issue @@ -637,7 +641,7 @@ func (p *Pages) RepoIssues(w io.Writer, params RepoIssuesParams) error { } type RepoSingleIssueParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string Issue db.Issue @@ -659,7 +663,7 @@ func (p *Pages) RepoSingleIssue(w io.Writer, params RepoSingleIssueParams) error } type RepoNewIssueParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string } @@ -670,7 +674,7 @@ func (p *Pages) RepoNewIssue(w io.Writer, params RepoNewIssueParams) error { } type EditIssueCommentParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Issue *db.Issue Comment *db.Comment @@ -681,7 +685,7 @@ func (p *Pages) EditIssueCommentFragment(w io.Writer, params EditIssueCommentPar } type SingleIssueCommentParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string RepoInfo repoinfo.RepoInfo Issue *db.Issue @@ -693,7 +697,7 @@ func (p *Pages) SingleIssueCommentFragment(w io.Writer, params SingleIssueCommen } type RepoNewPullParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Branches []types.Branch Active string @@ -705,7 +709,7 @@ func (p *Pages) RepoNewPull(w io.Writer, params RepoNewPullParams) error { } type RepoPullsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pulls []*db.Pull Active string @@ -737,7 +741,7 @@ func (r ResubmitResult) Unknown() bool { } type RepoSinglePullParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string DidHandleMap map[string]string @@ -752,7 +756,7 @@ func (p *Pages) RepoSinglePull(w io.Writer, params RepoSinglePullParams) error { } type RepoPullPatchParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string RepoInfo repoinfo.RepoInfo Pull *db.Pull @@ -767,7 +771,7 @@ func (p *Pages) RepoPullPatchPage(w io.Writer, params RepoPullPatchParams) error } type RepoPullInterdiffParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string RepoInfo repoinfo.RepoInfo Pull *db.Pull @@ -817,7 +821,7 @@ func (p *Pages) PullCompareForkBranchesFragment(w io.Writer, params PullCompareF } type PullResubmitParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pull *db.Pull SubmissionId int @@ -828,7 +832,7 @@ func (p *Pages) PullResubmitFragment(w io.Writer, params PullResubmitParams) err } type PullActionsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pull *db.Pull RoundNumber int @@ -841,7 +845,7 @@ func (p *Pages) PullActionsFragment(w io.Writer, params PullActionsParams) error } type PullNewCommentParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pull *db.Pull RoundNumber int diff --git a/appview/pages/templates/user/oauthlogin.html b/appview/pages/templates/user/oauthlogin.html new file mode 100644 index 00000000..1eaaead1 --- /dev/null +++ b/appview/pages/templates/user/oauthlogin.html @@ -0,0 +1,71 @@ +{{ define "user/oauthlogin" }} + + + + + + + + login + + +
+

+ tangled +

+

+ tightly-knit social coding. +

+
+
+ + + + Use your + Bluesky handle to log + in. You will then be redirected to your PDS to + complete authentication. + +
+ + +
+

+ Join our Discord or + IRC channel: + #tangled on Libera Chat. +

+

+
+ + +{{ end }} diff --git a/appview/settings/settings.go b/appview/settings/settings.go index 1fafa210..a2b57a28 100644 --- a/appview/settings/settings.go +++ b/appview/settings/settings.go @@ -13,10 +13,10 @@ import ( "github.com/go-chi/chi/v5" "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" "tangled.sh/tangled.sh/core/appview/email" "tangled.sh/tangled.sh/core/appview/middleware" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" comatproto "github.com/bluesky-social/indigo/api/atproto" @@ -27,7 +27,7 @@ import ( type Settings struct { Db *db.DB - Auth *auth.Auth + OAuth *oauth.OAuth Pages *pages.Pages Config *appview.Config } @@ -35,7 +35,7 @@ type Settings struct { func (s *Settings) Router() http.Handler { r := chi.NewRouter() - r.Use(middleware.AuthMiddleware(s.Auth)) + r.Use(middleware.AuthMiddleware(s.OAuth)) r.Get("/", s.settings) @@ -56,7 +56,7 @@ func (s *Settings) Router() http.Handler { } func (s *Settings) settings(w http.ResponseWriter, r *http.Request) { - user := s.Auth.GetUser(r) + user := s.OAuth.GetUser(r) pubKeys, err := db.GetPublicKeys(s.Db, user.Did) if err != nil { log.Println(err) @@ -79,7 +79,7 @@ func (s *Settings) buildVerificationEmail(emailAddr, did, code string) email.Ema verifyURL := s.verifyUrl(did, emailAddr, code) return email.Email{ - APIKey: s.Config.ResendApiKey, + APIKey: s.Config.Resend.ApiKey, From: "noreply@notifs.tangled.sh", To: emailAddr, Subject: "Verify your Tangled email", @@ -111,7 +111,7 @@ func (s *Settings) emails(w http.ResponseWriter, r *http.Request) { log.Println("unimplemented") return case http.MethodPut: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emAddr := r.FormValue("email") emAddr = strings.TrimSpace(emAddr) @@ -174,7 +174,7 @@ func (s *Settings) emails(w http.ResponseWriter, r *http.Request) { s.Pages.Notice(w, "settings-emails-success", "Click the link in the email we sent you to verify your email address.") return case http.MethodDelete: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emailAddr := r.FormValue("email") emailAddr = strings.TrimSpace(emailAddr) @@ -207,8 +207,8 @@ func (s *Settings) emails(w http.ResponseWriter, r *http.Request) { func (s *Settings) verifyUrl(did string, email string, code string) string { var appUrl string - if s.Config.Dev { - appUrl = "http://" + s.Config.ListenAddr + if s.Config.Core.Dev { + appUrl = "http://" + s.Config.Core.ListenAddr } else { appUrl = "https://tangled.sh" } @@ -252,7 +252,7 @@ func (s *Settings) emailsVerifyResend(w http.ResponseWriter, r *http.Request) { return } - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emAddr := r.FormValue("email") emAddr = strings.TrimSpace(emAddr) @@ -323,7 +323,7 @@ func (s *Settings) emailsVerifyResend(w http.ResponseWriter, r *http.Request) { } func (s *Settings) emailsPrimary(w http.ResponseWriter, r *http.Request) { - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emailAddr := r.FormValue("email") emailAddr = strings.TrimSpace(emailAddr) @@ -348,13 +348,17 @@ func (s *Settings) keys(w http.ResponseWriter, r *http.Request) { log.Println("unimplemented") return case http.MethodPut: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) key := r.FormValue("key") key = strings.TrimSpace(key) name := r.FormValue("name") - client, _ := s.Auth.AuthorizedClient(r) + client, err := s.OAuth.AuthorizedClient(r) + if err != nil { + s.Pages.Notice(w, "settings-keys", "Failed to authorize. Try again later.") + return + } - _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key)) + _, _, _, _, err = ssh.ParseAuthorizedKey([]byte(key)) if err != nil { log.Printf("parsing public key: %s", err) s.Pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a public key.") @@ -378,7 +382,7 @@ func (s *Settings) keys(w http.ResponseWriter, r *http.Request) { } // store in pds too - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.PublicKeyNSID, Repo: did, Rkey: rkey, @@ -409,7 +413,7 @@ func (s *Settings) keys(w http.ResponseWriter, r *http.Request) { return case http.MethodDelete: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) q := r.URL.Query() name := q.Get("name") @@ -420,7 +424,12 @@ func (s *Settings) keys(w http.ResponseWriter, r *http.Request) { log.Println(rkey) log.Println(key) - client, _ := s.Auth.AuthorizedClient(r) + client, err := s.OAuth.AuthorizedClient(r) + if err != nil { + log.Printf("failed to authorize client: %s", err) + s.Pages.Notice(w, "settings-keys", "Failed to authorize client.") + return + } if err := db.DeletePublicKey(s.Db, did, name, key); err != nil { log.Printf("removing public key: %s", err) @@ -430,7 +439,7 @@ func (s *Settings) keys(w http.ResponseWriter, r *http.Request) { if rkey != "" { // remove from pds too - _, err := comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err := client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.PublicKeyNSID, Repo: did, Rkey: rkey, diff --git a/appview/state/artifact.go b/appview/state/artifact.go index 020cba79..8cb629c6 100644 --- a/appview/state/artifact.go +++ b/appview/state/artifact.go @@ -22,7 +22,7 @@ import ( // TODO: proper statuses here on early exit func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) tagParam := chi.URLParam(r, "tag") f, err := s.fullyResolvedRepo(r) if err != nil { @@ -46,9 +46,14 @@ func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { } defer file.Close() - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "upload", "failed to get authorized client") + return + } - uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file) + uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file) if err != nil { log.Println("failed to upload blob", err) s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.") @@ -60,7 +65,7 @@ func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { rkey := appview.TID() createdAt := time.Now() - putRecordResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + putRecordResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoArtifactNSID, Repo: user.Did, Rkey: rkey, @@ -140,7 +145,11 @@ func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) { return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + return + } artifacts, err := db.GetArtifact( s.db, @@ -159,7 +168,7 @@ func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) { artifact := artifacts[0] - getBlobResp, err := comatproto.SyncGetBlob(r.Context(), client, artifact.BlobCid.String(), artifact.Did) + getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did) if err != nil { log.Println("failed to get blob from pds", err) return @@ -171,7 +180,7 @@ func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) { // TODO: proper statuses here on early exit func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) tagParam := chi.URLParam(r, "tag") filename := chi.URLParam(r, "file") f, err := s.fullyResolvedRepo(r) @@ -180,7 +189,7 @@ func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { return } - client, _ := s.auth.AuthorizedClient(r) + client, _ := s.oauth.AuthorizedClient(r) tag := plumbing.NewHash(tagParam) @@ -208,7 +217,7 @@ func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { return } - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.RepoArtifactNSID, Repo: user.Did, Rkey: artifact.Rkey, @@ -254,7 +263,7 @@ func (s *State) resolveTag(f *FullyResolvedRepo, tagParam string) (*types.TagRef return nil, err } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { return nil, err } diff --git a/appview/state/follow.go b/appview/state/follow.go index 74b5f5a3..4a99f8bd 100644 --- a/appview/state/follow.go +++ b/appview/state/follow.go @@ -14,7 +14,7 @@ import ( ) func (s *State) Follow(w http.ResponseWriter, r *http.Request) { - currentUser := s.auth.GetUser(r) + currentUser := s.oauth.GetUser(r) subject := r.URL.Query().Get("subject") if subject == "" { @@ -32,13 +32,17 @@ func (s *State) Follow(w http.ResponseWriter, r *http.Request) { return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to authorize client") + return + } switch r.Method { case http.MethodPost: createdAt := time.Now().Format(time.RFC3339) rkey := appview.TID() - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: currentUser.Did, Rkey: rkey, @@ -75,7 +79,7 @@ func (s *State) Follow(w http.ResponseWriter, r *http.Request) { return } - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: currentUser.Did, Rkey: follow.Rkey, diff --git a/appview/state/git_http.go b/appview/state/git_http.go index d9296fb0..8f0a0238 100644 --- a/appview/state/git_http.go +++ b/appview/state/git_http.go @@ -15,7 +15,7 @@ func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) { repo := chi.URLParam(r, "repo") scheme := "https" - if s.config.Dev { + if s.config.Core.Dev { scheme = "http" } targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, knot, user.DID, repo, r.URL.RawQuery) @@ -52,7 +52,7 @@ func (s *State) UploadPack(w http.ResponseWriter, r *http.Request) { repo := chi.URLParam(r, "repo") scheme := "https" - if s.config.Dev { + if s.config.Core.Dev { scheme = "http" } targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-pack?%s", scheme, knot, user.DID, repo, r.URL.RawQuery) diff --git a/appview/state/middleware.go b/appview/state/middleware.go index c3c5cdbe..1baabcf1 100644 --- a/appview/state/middleware.go +++ b/appview/state/middleware.go @@ -20,7 +20,7 @@ func knotRoleMiddleware(s *State, group string) middleware.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // requires auth also - actor := s.auth.GetUser(r) + actor := s.oauth.GetUser(r) if actor == nil { // we need a logged in user log.Printf("not logged in, redirecting") @@ -54,7 +54,7 @@ func RepoPermissionMiddleware(s *State, requiredPerm string) middleware.Middlewa return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // requires auth also - actor := s.auth.GetUser(r) + actor := s.oauth.GetUser(r) if actor == nil { // we need a logged in user log.Printf("not logged in, redirecting") diff --git a/appview/state/profile.go b/appview/state/profile.go index a5c3fc63..ebec4531 100644 --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -119,7 +119,7 @@ func (s *State) profilePage(w http.ResponseWriter, r *http.Request) { log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err) } - loggedInUser := s.auth.GetUser(r) + loggedInUser := s.oauth.GetUser(r) followStatus := db.IsNotFollowing if loggedInUser != nil { followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) @@ -161,7 +161,7 @@ func (s *State) reposPage(w http.ResponseWriter, r *http.Request) { log.Printf("getting repos for %s: %s", ident.DID.String(), err) } - loggedInUser := s.auth.GetUser(r) + loggedInUser := s.oauth.GetUser(r) followStatus := db.IsNotFollowing if loggedInUser != nil { followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) @@ -190,15 +190,15 @@ func (s *State) reposPage(w http.ResponseWriter, r *http.Request) { } func (s *State) GetAvatarUri(handle string) string { - secret := s.config.AvatarSharedSecret + secret := s.config.Avatar.SharedSecret h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(handle)) signature := hex.EncodeToString(h.Sum(nil)) - return fmt.Sprintf("%s/%s/%s", s.config.AvatarHost, signature, handle) + return fmt.Sprintf("%s/%s/%s", s.config.Avatar.Host, signature, handle) } func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) err := r.ParseForm() if err != nil { @@ -246,7 +246,7 @@ func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) { } func (s *State) UpdateProfilePins(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) err := r.ParseForm() if err != nil { @@ -286,7 +286,7 @@ func (s *State) UpdateProfilePins(w http.ResponseWriter, r *http.Request) { } func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) tx, err := s.db.BeginTx(r.Context(), nil) if err != nil { log.Println("failed to start transaction", err) @@ -294,7 +294,12 @@ func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *htt return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") + return + } // yeah... lexgen dose not support syntax.ATURI in the record for some reason, // nor does it support exact size arrays @@ -308,13 +313,13 @@ func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *htt vanityStats = append(vanityStats, string(v.Kind)) } - ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self") + ex, _ := client.RepoGetRecord(r.Context(), "", tangled.ActorProfileNSID, user.Did, "self") var cid *string if ex != nil { cid = ex.Cid } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.ActorProfileNSID, Repo: user.Did, Rkey: "self", @@ -347,7 +352,7 @@ func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *htt } func (s *State) EditBioFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) profile, err := db.GetProfile(s.db, user.Did) if err != nil { @@ -361,7 +366,7 @@ func (s *State) EditBioFragment(w http.ResponseWriter, r *http.Request) { } func (s *State) EditPinsFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) profile, err := db.GetProfile(s.db, user.Did) if err != nil { diff --git a/appview/state/pull.go b/appview/state/pull.go index db6667dc..7a08b697 100644 --- a/appview/state/pull.go +++ b/appview/state/pull.go @@ -13,8 +13,8 @@ import ( "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/patchutil" "tangled.sh/tangled.sh/core/types" @@ -29,7 +29,7 @@ import ( func (s *State) PullActions(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -73,7 +73,7 @@ func (s *State) PullActions(w http.ResponseWriter, r *http.Request) { } func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -143,7 +143,7 @@ func (s *State) mergeCheck(f *FullyResolvedRepo, pull *db.Pull) types.MergeCheck } } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err) return types.MergeCheckResponse{ @@ -215,7 +215,7 @@ func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.Resubmi repoName = f.RepoName } - us, err := NewUnsignedClient(knot, s.config.Dev) + us, err := NewUnsignedClient(knot, s.config.Core.Dev) if err != nil { log.Printf("failed to setup client for %s; ignoring: %v", knot, err) return pages.Unknown @@ -250,7 +250,7 @@ func (s *State) resubmitCheck(f *FullyResolvedRepo, pull *db.Pull) pages.Resubmi } func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -298,7 +298,7 @@ func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) { } func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -355,7 +355,7 @@ func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) { interdiff := patchutil.Interdiff(previousPatch, currentPatch) s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{ - LoggedInUser: s.auth.GetUser(r), + LoggedInUser: s.oauth.GetUser(r), RepoInfo: f.RepoInfo(s, user), Pull: pull, Round: roundIdInt, @@ -397,7 +397,7 @@ func (s *State) RepoPullPatchRaw(w http.ResponseWriter, r *http.Request) { } func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) params := r.URL.Query() state := db.PullOpen @@ -451,7 +451,7 @@ func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) { } s.pages.RepoPulls(w, pages.RepoPullsParams{ - LoggedInUser: s.auth.GetUser(r), + LoggedInUser: s.oauth.GetUser(r), RepoInfo: f.RepoInfo(s, user), Pulls: pulls, DidHandleMap: didHandleMap, @@ -461,7 +461,7 @@ func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) { } func (s *State) PullComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -519,8 +519,13 @@ func (s *State) PullComment(w http.ResponseWriter, r *http.Request) { } atUri := f.RepoAt.String() - client, _ := s.auth.AuthorizedClient(r) - atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "pull-comment", "Failed to create comment.") + return + } + atResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullCommentNSID, Repo: user.Did, Rkey: appview.TID(), @@ -568,7 +573,7 @@ func (s *State) PullComment(w http.ResponseWriter, r *http.Request) { } func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -577,7 +582,7 @@ func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", f.Knot) s.pages.Error503(w) @@ -646,7 +651,7 @@ func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client to %s: %v", f.Knot, err) s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") @@ -689,7 +694,7 @@ func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { } } -func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) { +func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, sourceBranch string) { pullSource := &db.PullSource{ Branch: sourceBranch, } @@ -698,7 +703,7 @@ func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f } // Generate a patch using /compare - ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) + ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", f.Knot, err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") @@ -723,7 +728,7 @@ func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource) } -func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) { +func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string) { if !patchutil.IsPatchValid(patch) { s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") return @@ -732,7 +737,7 @@ func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f * s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil) } -func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { +func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { fork, err := db.GetForkByDid(s.db, user.Did, forkRepo) if errors.Is(err, sql.ErrNoRows) { s.pages.Notice(w, "pull", "No such fork.") @@ -750,14 +755,14 @@ func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *F return } - sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev) + sc, err := NewSignedClient(fork.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create signed client:", err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") return } - us, err := NewUnsignedClient(fork.Knot, s.config.Dev) + us, err := NewUnsignedClient(fork.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client:", err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") @@ -816,7 +821,7 @@ func (s *State) createPullRequest( w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, - user *auth.User, + user *oauth.User, title, body, targetBranch string, patch string, sourceRev string, @@ -870,7 +875,12 @@ func (s *State) createPullRequest( s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") + return + } pullId, err := db.NextPullId(s.db, f.RepoAt) if err != nil { log.Println("failed to get pull id", err) @@ -878,7 +888,7 @@ func (s *State) createPullRequest( return } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: rkey, @@ -929,7 +939,7 @@ func (s *State) ValidatePatch(w http.ResponseWriter, r *http.Request) { } func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -942,14 +952,14 @@ func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) { } func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", f.Knot) s.pages.Error503(w) @@ -982,7 +992,7 @@ func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) } func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1002,7 +1012,7 @@ func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) { } func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -1019,7 +1029,7 @@ func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Requ return } - sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev) + sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", repo.Knot) s.pages.Error503(w) @@ -1046,7 +1056,7 @@ func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Requ return } - targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev) + targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for target knot %s", f.Knot) s.pages.Error503(w) @@ -1081,7 +1091,7 @@ func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Requ } func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1117,7 +1127,7 @@ func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) { } func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) pull, ok := r.Context().Value("pull").(*db.Pull) if !ok { @@ -1159,16 +1169,21 @@ func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) { s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return + } - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) if err != nil { // failed to get record s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") return } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: pull.Rkey, @@ -1200,7 +1215,7 @@ func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) { } func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) pull, ok := r.Context().Value("pull").(*db.Pull) if !ok { @@ -1227,7 +1242,7 @@ func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { return } - ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) + ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create client for %s: %s", f.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") @@ -1268,9 +1283,14 @@ func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to authorize client") + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return + } - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) if err != nil { // failed to get record s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") @@ -1280,7 +1300,7 @@ func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { recordPullSource := &tangled.RepoPull_Source{ Branch: pull.PullSource.Branch, } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: pull.Rkey, @@ -1313,7 +1333,7 @@ func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { } func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) pull, ok := r.Context().Value("pull").(*db.Pull) if !ok { @@ -1342,7 +1362,7 @@ func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { } // extract patch by performing compare - ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev) + ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create client for %s: %s", forkRepo.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") @@ -1357,7 +1377,7 @@ func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { } // update the hidden tracking branch to latest - signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev) + signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") @@ -1406,9 +1426,14 @@ func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get client") + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return + } - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) if err != nil { // failed to get record s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") @@ -1420,7 +1445,7 @@ func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { Branch: pull.PullSource.Branch, Repo: &repoAt, } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: pull.Rkey, @@ -1503,7 +1528,7 @@ func (s *State) MergePull(w http.ResponseWriter, r *http.Request) { log.Printf("failed to get primary email: %s", err) } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", f.Knot, err) s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") @@ -1533,7 +1558,7 @@ func (s *State) MergePull(w http.ResponseWriter, r *http.Request) { } func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -1587,7 +1612,7 @@ func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) { } func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { diff --git a/appview/state/repo.go b/appview/state/repo.go index 6a85925f..9c0fa296 100644 --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -18,8 +18,8 @@ import ( "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/appview/pages/markup" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" @@ -45,7 +45,7 @@ func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", f.Knot) s.pages.Error503(w) @@ -119,7 +119,7 @@ func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { emails := uniqueEmails(commitsTrunc) - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoIndexPage(w, pages.RepoIndexParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -150,7 +150,7 @@ func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) { ref := chi.URLParam(r, "ref") - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) return @@ -190,7 +190,7 @@ func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) { tagMap[hash] = append(tagMap[hash], tag.Name) } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoLog(w, pages.RepoLogParams{ LoggedInUser: user, TagMap: tagMap, @@ -209,7 +209,7 @@ func (s *State) RepoDescriptionEdit(w http.ResponseWriter, r *http.Request) { return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{ RepoInfo: f.RepoInfo(s, user), }) @@ -232,7 +232,7 @@ func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) { return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) switch r.Method { case http.MethodGet: @@ -241,9 +241,14 @@ func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) { }) return case http.MethodPut: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) newDescription := r.FormValue("description") - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get client") + s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") + return + } // optimistic update err = db.UpdateDescription(s.db, string(repoAt), newDescription) @@ -256,13 +261,13 @@ func (s *State) RepoDescription(w http.ResponseWriter, r *http.Request) { // this is a bit of a pain because the golang atproto impl does not allow nil SwapRecord field // // SwapRecord is optional and should happen automagically, but given that it does not, we have to perform two requests - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, user.Did, rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey) if err != nil { // failed to get record s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.") return } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: rkey, @@ -303,7 +308,7 @@ func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) { } ref := chi.URLParam(r, "ref") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } @@ -331,7 +336,7 @@ func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) { return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoCommit(w, pages.RepoCommitParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -351,7 +356,7 @@ func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) { ref := chi.URLParam(r, "ref") treePath := chi.URLParam(r, "*") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath)) @@ -380,7 +385,7 @@ func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) { return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) var breadcrumbs [][]string breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)}) @@ -411,7 +416,7 @@ func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) { return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) return @@ -451,7 +456,7 @@ func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) { } } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoTags(w, pages.RepoTagsParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -469,7 +474,7 @@ func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) { return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) return @@ -511,7 +516,7 @@ func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) { return strings.Compare(a.Name, b.Name) * -1 }) - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoBranches(w, pages.RepoBranchesParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -530,7 +535,7 @@ func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) { ref := chi.URLParam(r, "ref") filePath := chi.URLParam(r, "*") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) @@ -568,7 +573,7 @@ func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) { showRendered = r.URL.Query().Get("code") != "true" } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoBlob(w, pages.RepoBlobParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -591,7 +596,7 @@ func (s *State) RepoBlobRaw(w http.ResponseWriter, r *http.Request) { filePath := chi.URLParam(r, "*") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) @@ -652,7 +657,7 @@ func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) { return } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", f.Knot) return @@ -714,7 +719,7 @@ func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) { } func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -723,9 +728,13 @@ func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { } // remove record from pds - xrpcClient, _ := s.auth.AuthorizedClient(r) + xrpcClient, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + return + } repoRkey := f.RepoAt.RecordKey().String() - _, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{ + _, err = xrpcClient.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: repoRkey, @@ -743,7 +752,7 @@ func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { return } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", f.Knot) return @@ -838,7 +847,7 @@ func (s *State) SetDefaultBranch(w http.ResponseWriter, r *http.Request) { return } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", f.Knot) return @@ -868,7 +877,7 @@ func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: // for now, this is just pubkeys - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) repoCollaborators, err := f.Collaborators(r.Context(), s) if err != nil { log.Println("failed to get collaborators", err) @@ -884,7 +893,7 @@ func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) { var branchNames []string var defaultBranch string - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) } else { @@ -1008,7 +1017,7 @@ func (f *FullyResolvedRepo) Collaborators(ctx context.Context, s *State) ([]page return collaborators, nil } -func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo { +func (f *FullyResolvedRepo) RepoInfo(s *State, u *oauth.User) repoinfo.RepoInfo { isStarred := false if u != nil { isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt)) @@ -1051,7 +1060,7 @@ func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo { knot := f.Knot var disableFork bool - us, err := NewUnsignedClient(knot, s.config.Dev) + us, err := NewUnsignedClient(knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s: %v", knot, err) } else { @@ -1105,7 +1114,7 @@ func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo { } func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1159,7 +1168,7 @@ func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { } func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1195,8 +1204,12 @@ func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { closed := tangled.RepoIssueStateClosed - client, _ := s.auth.AuthorizedClient(r) - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + return + } + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueStateNSID, Repo: user.Did, Rkey: appview.TID(), @@ -1214,7 +1227,7 @@ func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { return } - err := db.CloseIssue(s.db, f.RepoAt, issueIdInt) + err = db.CloseIssue(s.db, f.RepoAt, issueIdInt) if err != nil { log.Println("failed to close issue", err) s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") @@ -1231,7 +1244,7 @@ func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { } func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1279,7 +1292,7 @@ func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { } func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1330,8 +1343,13 @@ func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { } atUri := f.RepoAt.String() - client, _ := s.auth.AuthorizedClient(r) - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issue-comment", "Failed to create comment.") + return + } + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueCommentNSID, Repo: user.Did, Rkey: rkey, @@ -1358,7 +1376,7 @@ func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { } func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1417,7 +1435,7 @@ func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { } func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1469,7 +1487,12 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { case http.MethodPost: // extract form value newBody := r.FormValue("body") - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issue-comment", "Failed to create comment.") + return + } rkey := comment.Rkey // optimistic update @@ -1484,7 +1507,7 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { // rkey is optional, it was introduced later if comment.Rkey != "" { // update the record on pds - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoIssueCommentNSID, user.Did, rkey) if err != nil { // failed to get record log.Println(err, rkey) @@ -1499,7 +1522,7 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { createdAt := record["createdAt"].(string) commentIdInt64 := int64(commentIdInt) - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueCommentNSID, Repo: user.Did, Rkey: rkey, @@ -1542,7 +1565,7 @@ func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { } func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1599,8 +1622,13 @@ func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { // delete from pds if comment.Rkey != "" { - client, _ := s.auth.AuthorizedClient(r) - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issue-comment", "Failed to delete comment.") + return + } + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: user.Did, Rkey: comment.Rkey, @@ -1647,7 +1675,7 @@ func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) { page = pagination.FirstPage() } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1676,7 +1704,7 @@ func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) { } s.pages.RepoIssues(w, pages.RepoIssuesParams{ - LoggedInUser: s.auth.GetUser(r), + LoggedInUser: s.oauth.GetUser(r), RepoInfo: f.RepoInfo(s, user), Issues: issues, DidHandleMap: didHandleMap, @@ -1687,7 +1715,7 @@ func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) { } func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -1735,9 +1763,14 @@ func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issues", "Failed to create issue.") + return + } atUri := f.RepoAt.String() - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueNSID, Repo: user.Did, Rkey: appview.TID(), @@ -1770,7 +1803,7 @@ func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { } func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Printf("failed to resolve source repo: %v", err) @@ -1779,7 +1812,7 @@ func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) knots, err := s.enforcer.GetDomainsForUser(user.Did) if err != nil { s.pages.Notice(w, "repo", "Invalid user account.") @@ -1829,14 +1862,14 @@ func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { return } - client, err := NewSignedClient(knot, secret, s.config.Dev) + client, err := NewSignedClient(knot, secret, s.config.Core.Dev) if err != nil { s.pages.Notice(w, "repo", "Failed to reach knot server.") return } var uri string - if s.config.Dev { + if s.config.Core.Dev { uri = "http" } else { uri = "https" @@ -1883,10 +1916,15 @@ func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { // continue } - xrpcClient, _ := s.auth.AuthorizedClient(r) + xrpcClient, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "repo", "Failed to create repository.") + return + } createdAt := time.Now().Format(time.RFC3339) - atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ + atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: rkey, diff --git a/appview/state/repo_util.go b/appview/state/repo_util.go index 9ca3b2de..df8d8679 100644 --- a/appview/state/repo_util.go +++ b/appview/state/repo_util.go @@ -12,8 +12,8 @@ import ( "github.com/bluesky-social/indigo/atproto/syntax" "github.com/go-chi/chi/v5" "github.com/go-git/go-git/v5/plumbing/object" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" ) @@ -45,7 +45,7 @@ func (s *State) fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) { ref := chi.URLParam(r, "ref") if ref == "" { - us, err := NewUnsignedClient(knot, s.config.Dev) + us, err := NewUnsignedClient(knot, s.config.Core.Dev) if err != nil { return nil, err } @@ -73,7 +73,7 @@ func (s *State) fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) { }, nil } -func RolesInRepo(s *State, u *auth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo { +func RolesInRepo(s *State, u *oauth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo { if u != nil { r := s.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo()) return repoinfo.RolesInRepo{r} diff --git a/appview/state/router.go b/appview/state/router.go index 788ea62b..44605b4b 100644 --- a/appview/state/router.go +++ b/appview/state/router.go @@ -5,7 +5,9 @@ import ( "strings" "github.com/go-chi/chi/v5" + "github.com/gorilla/sessions" "tangled.sh/tangled.sh/core/appview/middleware" + oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler" "tangled.sh/tangled.sh/core/appview/settings" "tangled.sh/tangled.sh/core/appview/state/userutil" ) @@ -67,7 +69,7 @@ func (s *State) UserRouter() http.Handler { r.Route("/tags", func(r chi.Router) { r.Get("/", s.RepoTags) r.Route("/{tag}", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) // require auth to download for now r.Get("/download/{file}", s.DownloadArtifact) @@ -90,7 +92,7 @@ func (s *State) UserRouter() http.Handler { r.Get("/{issue}", s.RepoSingleIssue) r.Group(func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/new", s.NewIssue) r.Post("/new", s.NewIssue) r.Post("/{issue}/comment", s.NewIssueComment) @@ -106,14 +108,14 @@ func (s *State) UserRouter() http.Handler { }) r.Route("/fork", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/", s.ForkRepo) r.Post("/", s.ForkRepo) }) r.Route("/pulls", func(r chi.Router) { r.Get("/", s.RepoPulls) - r.With(middleware.AuthMiddleware(s.auth)).Route("/new", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/new", func(r chi.Router) { r.Get("/", s.NewPull) r.Get("/patch-upload", s.PatchUploadFragment) r.Post("/validate-patch", s.ValidatePatch) @@ -131,7 +133,7 @@ func (s *State) UserRouter() http.Handler { r.Get("/", s.RepoPullPatch) r.Get("/interdiff", s.RepoPullInterdiff) r.Get("/actions", s.PullActions) - r.With(middleware.AuthMiddleware(s.auth)).Route("/comment", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/comment", func(r chi.Router) { r.Get("/", s.PullComment) r.Post("/", s.PullComment) }) @@ -142,7 +144,7 @@ func (s *State) UserRouter() http.Handler { }) r.Group(func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Route("/resubmit", func(r chi.Router) { r.Get("/", s.ResubmitPull) r.Post("/", s.ResubmitPull) @@ -165,7 +167,7 @@ func (s *State) UserRouter() http.Handler { // settings routes, needs auth r.Group(func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) // repo description can only be edited by owner r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) { r.Put("/", s.RepoDescription) @@ -196,15 +198,15 @@ func (s *State) StandardRouter() http.Handler { r.Get("/", s.Timeline) - r.With(middleware.AuthMiddleware(s.auth)).Post("/logout", s.Logout) + r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout) - r.Route("/login", func(r chi.Router) { - r.Get("/", s.Login) - r.Post("/", s.Login) - }) + // r.Route("/login", func(r chi.Router) { + // r.Get("/", s.Login) + // r.Post("/", s.Login) + // }) r.Route("/knots", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/", s.Knots) r.Post("/key", s.RegistrationKey) @@ -222,25 +224,25 @@ func (s *State) StandardRouter() http.Handler { r.Route("/repo", func(r chi.Router) { r.Route("/new", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/", s.NewRepo) r.Post("/", s.NewRepo) }) // r.Post("/import", s.ImportRepo) }) - r.With(middleware.AuthMiddleware(s.auth)).Route("/follow", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) { r.Post("/", s.Follow) r.Delete("/", s.Follow) }) - r.With(middleware.AuthMiddleware(s.auth)).Route("/star", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) { r.Post("/", s.Star) r.Delete("/", s.Star) }) r.Route("/profile", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/edit-bio", s.EditBioFragment) r.Get("/edit-pins", s.EditPinsFragment) r.Post("/bio", s.UpdateProfileBio) @@ -248,7 +250,7 @@ func (s *State) StandardRouter() http.Handler { }) r.Mount("/settings", s.SettingsRouter()) - + r.Mount("/oauth", s.OAuthRouter()) r.Get("/keys/{user}", s.Keys) r.NotFound(func(w http.ResponseWriter, r *http.Request) { @@ -257,10 +259,23 @@ func (s *State) StandardRouter() http.Handler { return r } +func (s *State) OAuthRouter() http.Handler { + oauth := &oauthhandler.OAuthHandler{ + Config: s.config, + Pages: s.pages, + Resolver: s.resolver, + Db: s.db, + Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), + OAuth: s.oauth, + } + + return oauth.Router() +} + func (s *State) SettingsRouter() http.Handler { settings := &settings.Settings{ Db: s.db, - Auth: s.auth, + OAuth: s.oauth, Pages: s.pages, Config: s.config, } diff --git a/appview/state/star.go b/appview/state/star.go index 08969f2d..7f4635fa 100644 --- a/appview/state/star.go +++ b/appview/state/star.go @@ -15,7 +15,7 @@ import ( ) func (s *State) Star(w http.ResponseWriter, r *http.Request) { - currentUser := s.auth.GetUser(r) + currentUser := s.oauth.GetUser(r) subject := r.URL.Query().Get("subject") if subject == "" { @@ -29,13 +29,17 @@ func (s *State) Star(w http.ResponseWriter, r *http.Request) { return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to authorize client", err) + return + } switch r.Method { case http.MethodPost: createdAt := time.Now().Format(time.RFC3339) rkey := appview.TID() - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.FeedStarNSID, Repo: currentUser.Did, Rkey: rkey, @@ -80,7 +84,7 @@ func (s *State) Star(w http.ResponseWriter, r *http.Request) { return } - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.FeedStarNSID, Repo: currentUser.Did, Rkey: star.Rkey, diff --git a/appview/state/state.go b/appview/state/state.go index 7f901f48..7bb64d7d 100644 --- a/appview/state/state.go +++ b/appview/state/state.go @@ -21,6 +21,7 @@ import ( "tangled.sh/tangled.sh/core/appview" "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/jetstream" "tangled.sh/tangled.sh/core/rbac" @@ -29,8 +30,9 @@ import ( type State struct { db *db.DB auth *auth.Auth + oauth *oauth.OAuth enforcer *rbac.Enforcer - tidClock *syntax.TIDClock + tidClock syntax.TIDClock pages *pages.Pages resolver *appview.Resolver jc *jetstream.JetstreamClient @@ -38,17 +40,17 @@ type State struct { } func Make(config *appview.Config) (*State, error) { - d, err := db.Make(config.DbPath) + d, err := db.Make(config.Core.DbPath) if err != nil { return nil, err } - auth, err := auth.Make(config.CookieSecret) + auth, err := auth.Make(config.Core.CookieSecret) if err != nil { return nil, err } - enforcer, err := rbac.NewEnforcer(config.DbPath) + enforcer, err := rbac.NewEnforcer(config.Core.DbPath) if err != nil { return nil, err } @@ -59,9 +61,11 @@ func Make(config *appview.Config) (*State, error) { resolver := appview.NewResolver() + oauth := oauth.NewOAuth(d, config) + wrapper := db.DbWrapper{d} jc, err := jetstream.NewJetstreamClient( - config.JetstreamEndpoint, + config.Jetstream.Endpoint, "appview", []string{ tangled.GraphFollowNSID, @@ -86,6 +90,7 @@ func Make(config *appview.Config) (*State, error) { state := &State{ d, auth, + oauth, enforcer, clock, pgs, @@ -101,90 +106,90 @@ func TID(c *syntax.TIDClock) string { return c.Next().String() } -func (s *State) Login(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - switch r.Method { - case http.MethodGet: - err := s.pages.Login(w, pages.LoginParams{}) - if err != nil { - log.Printf("rendering login page: %s", err) - } - - return - case http.MethodPost: - handle := strings.TrimPrefix(r.FormValue("handle"), "@") - appPassword := r.FormValue("app_password") - - resolved, err := s.resolver.ResolveIdent(ctx, handle) - if err != nil { - log.Println("failed to resolve handle:", err) - s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) - return - } - - atSession, err := s.auth.CreateInitialSession(ctx, resolved, appPassword) - if err != nil { - s.pages.Notice(w, "login-msg", "Invalid handle or password.") - return - } - sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} - - err = s.auth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) - if err != nil { - s.pages.Notice(w, "login-msg", "Failed to login, try again later.") - return - } - - log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) - - did := resolved.DID.String() - defaultKnot := "knot1.tangled.sh" - - go func() { - log.Printf("adding %s to default knot", did) - err = s.enforcer.AddMember(defaultKnot, did) - if err != nil { - log.Println("failed to add user to knot1.tangled.sh: ", err) - return - } - err = s.enforcer.E.SavePolicy() - if err != nil { - log.Println("failed to add user to knot1.tangled.sh: ", err) - return - } - - secret, err := db.GetRegistrationKey(s.db, defaultKnot) - if err != nil { - log.Println("failed to get registration key for knot1.tangled.sh") - return - } - signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Dev) - resp, err := signedClient.AddMember(did) - if err != nil { - log.Println("failed to add user to knot1.tangled.sh: ", err) - return - } - - if resp.StatusCode != http.StatusNoContent { - log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) - return - } - }() - - s.pages.HxRedirect(w, "/") - return - } -} +// func (s *State) Login(w http.ResponseWriter, r *http.Request) { +// ctx := r.Context() + +// switch r.Method { +// case http.MethodGet: +// err := s.pages.Login(w, pages.LoginParams{}) +// if err != nil { +// log.Printf("rendering login page: %s", err) +// } + +// return +// case http.MethodPost: +// handle := strings.TrimPrefix(r.FormValue("handle"), "@") +// appPassword := r.FormValue("app_password") + +// resolved, err := s.resolver.ResolveIdent(ctx, handle) +// if err != nil { +// log.Println("failed to resolve handle:", err) +// s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) +// return +// } + +// atSession, err := s.oauth.CreateInitialSession(ctx, resolved, appPassword) +// if err != nil { +// s.pages.Notice(w, "login-msg", "Invalid handle or password.") +// return +// } +// sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} + +// err = s.oauth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) +// if err != nil { +// s.pages.Notice(w, "login-msg", "Failed to login, try again later.") +// return +// } + +// log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) + +// did := resolved.DID.String() +// defaultKnot := "knot1.tangled.sh" + +// go func() { +// log.Printf("adding %s to default knot", did) +// err = s.enforcer.AddMember(defaultKnot, did) +// if err != nil { +// log.Println("failed to add user to knot1.tangled.sh: ", err) +// return +// } +// err = s.enforcer.E.SavePolicy() +// if err != nil { +// log.Println("failed to add user to knot1.tangled.sh: ", err) +// return +// } + +// secret, err := db.GetRegistrationKey(s.db, defaultKnot) +// if err != nil { +// log.Println("failed to get registration key for knot1.tangled.sh") +// return +// } +// signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Core.Dev) +// resp, err := signedClient.AddMember(did) +// if err != nil { +// log.Println("failed to add user to knot1.tangled.sh: ", err) +// return +// } + +// if resp.StatusCode != http.StatusNoContent { +// log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) +// return +// } +// }() + +// s.pages.HxRedirect(w, "/") +// return +// } +// } func (s *State) Logout(w http.ResponseWriter, r *http.Request) { - s.auth.ClearSession(r, w) + s.oauth.ClearSession(r, w) w.Header().Set("HX-Redirect", "/login") w.WriteHeader(http.StatusSeeOther) } func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) timeline, err := db.MakeTimeline(s.db) if err != nil { @@ -235,7 +240,7 @@ func (s *State) RegistrationKey(w http.ResponseWriter, r *http.Request) { return case http.MethodPost: - session, err := s.auth.Store.Get(r, appview.SessionName) + session, err := s.oauth.Store.Get(r, appview.SessionName) if err != nil || session.IsNew { log.Println("unauthorized attempt to generate registration key") http.Error(w, "Forbidden", http.StatusUnauthorized) @@ -297,7 +302,7 @@ func (s *State) Keys(w http.ResponseWriter, r *http.Request) { // create a signed request and check if a node responds to that func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) domain := chi.URLParam(r, "domain") if domain == "" { @@ -312,7 +317,7 @@ func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) { return } - client, err := NewSignedClient(domain, secret, s.config.Dev) + client, err := NewSignedClient(domain, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", domain) } @@ -421,7 +426,7 @@ func (s *State) KnotServerInfo(w http.ResponseWriter, r *http.Request) { return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) reg, err := db.RegistrationByDomain(s.db, domain) if err != nil { w.Write([]byte("failed to pull up registration info")) @@ -469,7 +474,7 @@ func (s *State) KnotServerInfo(w http.ResponseWriter, r *http.Request) { // get knots registered by this user func (s *State) Knots(w http.ResponseWriter, r *http.Request) { // for now, this is just pubkeys - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) registrations, err := db.RegistrationsByDid(s.db, user.Did) if err != nil { log.Println(err) @@ -522,10 +527,14 @@ func (s *State) AddMember(w http.ResponseWriter, r *http.Request) { log.Printf("adding %s to %s\n", subjectIdentity.Handle.String(), domain) // announce this relation into the firehose, store into owners' pds - client, _ := s.auth.AuthorizedClient(r) - currentUser := s.auth.GetUser(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + http.Error(w, "failed to authorize client", http.StatusInternalServerError) + return + } + currentUser := s.oauth.GetUser(r) createdAt := time.Now().Format(time.RFC3339) - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.KnotMemberNSID, Repo: currentUser.Did, Rkey: appview.TID(), @@ -550,7 +559,7 @@ func (s *State) AddMember(w http.ResponseWriter, r *http.Request) { return } - ksClient, err := NewSignedClient(domain, secret, s.config.Dev) + ksClient, err := NewSignedClient(domain, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", domain) return @@ -614,7 +623,7 @@ func validateRepoName(name string) error { func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) knots, err := s.enforcer.GetDomainsForUser(user.Did) if err != nil { s.pages.Notice(w, "repo", "Invalid user account.") @@ -627,7 +636,7 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { }) case http.MethodPost: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) domain := r.FormValue("domain") if domain == "" { @@ -671,7 +680,7 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { return } - client, err := NewSignedClient(domain, secret, s.config.Dev) + client, err := NewSignedClient(domain, secret, s.config.Core.Dev) if err != nil { s.pages.Notice(w, "repo", "Failed to connect to knot server.") return @@ -686,10 +695,14 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { Description: description, } - xrpcClient, _ := s.auth.AuthorizedClient(r) + xrpcClient, err := s.oauth.AuthorizedClient(r) + if err != nil { + s.pages.Notice(w, "repo", "Failed to write record to PDS.") + return + } createdAt := time.Now().Format(time.RFC3339) - atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ + atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: rkey, diff --git a/appview/tid.go b/appview/tid.go index 46a4da67..7d0f0269 100644 --- a/appview/tid.go +++ b/appview/tid.go @@ -4,7 +4,7 @@ import ( "github.com/bluesky-social/indigo/atproto/syntax" ) -var c *syntax.TIDClock = syntax.NewTIDClock(0) +var c syntax.TIDClock = syntax.NewTIDClock(0) func TID() string { return c.Next().String() diff --git a/cmd/appview/main.go b/cmd/appview/main.go index 2cc33c24..98b7a556 100644 --- a/cmd/appview/main.go +++ b/cmd/appview/main.go @@ -26,6 +26,6 @@ func main() { log.Fatal(err) } - log.Println("starting server on", c.ListenAddr) - log.Println(http.ListenAndServe(c.ListenAddr, state.Router())) + log.Println("starting server on", c.Core.ListenAddr) + log.Println(http.ListenAndServe(c.Core.ListenAddr, state.Router())) } -- 2.51.2