this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add simple auth for new admin routes and basic auth token management (#133)

Adding the scaffolding for a simple admin token auth for control routes.
Actual routes TBD, but just wanting to land the auth first as a separate
thing.

authored by

Whyrusleeping and committed by
GitHub
8915cccc d698626a

+201 -35
+36
bgs/admin.go
··· 1 + package bgs 2 + 3 + import ( 4 + "strconv" 5 + 6 + "github.com/bluesky-social/indigo/util" 7 + "github.com/labstack/echo/v4" 8 + ) 9 + 10 + func (bgs *BGS) handleAdminDeleteRecord(e echo.Context) error { 11 + puri, err := util.ParseAtUri(e.QueryParam("uri")) 12 + if err != nil { 13 + return err 14 + } 15 + 16 + _ = puri 17 + 18 + panic("TODO") 19 + } 20 + 21 + func (bgs *BGS) handleAdminBlockRepoStream(e echo.Context) error { 22 + panic("TODO") 23 + } 24 + 25 + func (bgs *BGS) handleAdminDisableNewSlurps(e echo.Context) error { 26 + enabled, err := strconv.ParseBool(e.QueryParam("enabled")) 27 + if err != nil { 28 + return err 29 + } 30 + 31 + return bgs.slurper.SetNewSubsDisabled(!enabled) 32 + } 33 + 34 + func (bgs *BGS) handleAdminTakedownRepo(e echo.Context) error { 35 + panic("TODO") 36 + }
+66 -2
bgs/bgs.go
··· 60 60 61 61 func NewBGS(db *gorm.DB, ix *indexer.Indexer, repoman *repomgr.RepoManager, evtman *events.EventManager, didr plc.DidResolver, blobs blobs.BlobStore, ssl bool) (*BGS, error) { 62 62 db.AutoMigrate(User{}) 63 + db.AutoMigrate(AuthToken{}) 63 64 db.AutoMigrate(models.PDS{}) 64 65 65 66 bgs := &BGS{ ··· 73 74 } 74 75 75 76 ix.CreateExternalUser = bgs.createExternalUser 76 - bgs.slurper = NewSlurper(db, bgs.handleFedEvent, ssl) 77 + s, err := NewSlurper(db, bgs.handleFedEvent, ssl) 78 + if err != nil { 79 + return nil, err 80 + } 81 + 82 + bgs.slurper = s 77 83 78 84 if err := bgs.slurper.RestartAll(); err != nil { 79 85 return nil, err ··· 181 187 // TODO: this API is temporary until we formalize what we want here 182 188 183 189 e.GET("/xrpc/com.atproto.sync.subscribeRepos", bgs.EventsHandler) 184 - 185 190 e.GET("/xrpc/com.atproto.sync.getCheckout", bgs.HandleComAtprotoSyncGetCheckout) 186 191 e.GET("/xrpc/com.atproto.sync.getCommitPath", bgs.HandleComAtprotoSyncGetCommitPath) 187 192 e.GET("/xrpc/com.atproto.sync.getHead", bgs.HandleComAtprotoSyncGetHead) ··· 191 196 e.GET("/xrpc/com.atproto.sync.requestCrawl", bgs.HandleComAtprotoSyncRequestCrawl) 192 197 e.GET("/xrpc/com.atproto.sync.notifyOfUpdate", bgs.HandleComAtprotoSyncNotifyOfUpdate) 193 198 e.GET("/xrpc/_health", bgs.HandleHealthCheck) 199 + 200 + admin := e.Group("/admin", bgs.checkAdminAuth) 201 + admin.POST("/deleteRecord", bgs.handleAdminDeleteRecord) 194 202 195 203 return e.Start(listen) 196 204 } ··· 206 214 return c.JSON(500, HealthStatus{Status: "error", Message: "can't connect to database"}) 207 215 } else { 208 216 return c.JSON(200, HealthStatus{Status: "ok"}) 217 + } 218 + } 219 + 220 + type AuthToken struct { 221 + gorm.Model 222 + Token string `gorm:"index"` 223 + } 224 + 225 + func (bgs *BGS) lookupAdminToken(tok string) (bool, error) { 226 + var at AuthToken 227 + if err := bgs.db.Find(&at, "token = ?", tok).Error; err != nil { 228 + return false, err 229 + } 230 + 231 + if at.ID == 0 { 232 + return false, nil 233 + } 234 + 235 + return true, nil 236 + } 237 + 238 + func (bgs *BGS) CreateAdminToken(tok string) error { 239 + exists, err := bgs.lookupAdminToken(tok) 240 + if err != nil { 241 + return err 242 + } 243 + 244 + if exists { 245 + return nil 246 + } 247 + 248 + return bgs.db.Create(&AuthToken{ 249 + Token: tok, 250 + }).Error 251 + } 252 + 253 + func (bgs *BGS) checkAdminAuth(next echo.HandlerFunc) echo.HandlerFunc { 254 + return func(e echo.Context) error { 255 + authheader := e.Request().Header.Get("Authorization") 256 + pref := "Bearer " 257 + if !strings.HasPrefix(authheader, pref) { 258 + return echo.ErrForbidden 259 + } 260 + 261 + token := authheader[len(pref):] 262 + 263 + exists, err := bgs.lookupAdminToken(token) 264 + if err != nil { 265 + return err 266 + } 267 + 268 + if !exists { 269 + return echo.ErrForbidden 270 + } 271 + 272 + return next(e) 209 273 } 210 274 } 211 275
+51 -3
bgs/fedmgr.go
··· 27 27 lk sync.Mutex 28 28 active map[string]*models.PDS 29 29 30 + newSubsDisabled bool 31 + 30 32 ssl bool 31 33 } 32 34 33 - func NewSlurper(db *gorm.DB, cb IndexCallback, ssl bool) *Slurper { 34 - return &Slurper{ 35 + func NewSlurper(db *gorm.DB, cb IndexCallback, ssl bool) (*Slurper, error) { 36 + db.AutoMigrate(&SlurpConfig{}) 37 + s := &Slurper{ 35 38 cb: cb, 36 39 db: db, 37 40 active: make(map[string]*models.PDS), 38 41 ssl: ssl, 39 42 } 43 + if err := s.loadConfig(); err != nil { 44 + return nil, err 45 + } 46 + 47 + return s, nil 40 48 } 41 49 50 + func (s *Slurper) loadConfig() error { 51 + var sc SlurpConfig 52 + if err := s.db.Find(&sc).Error; err != nil { 53 + return err 54 + } 55 + 56 + if sc.ID == 0 { 57 + if err := s.db.Create(&SlurpConfig{}).Error; err != nil { 58 + return err 59 + } 60 + } 61 + 62 + s.newSubsDisabled = sc.NewSubsDisabled 63 + 64 + return nil 65 + } 66 + 67 + type SlurpConfig struct { 68 + gorm.Model 69 + 70 + NewSubsDisabled bool 71 + } 72 + 73 + func (s *Slurper) SetNewSubsDisabled(dis bool) error { 74 + s.lk.Lock() 75 + defer s.lk.Unlock() 76 + 77 + if err := s.db.Model(SlurpConfig{}).Where("id = 1").Update("new_subs_disabled", dis).Error; err != nil { 78 + return err 79 + } 80 + 81 + s.newSubsDisabled = dis 82 + return nil 83 + } 84 + 85 + var ErrNewSubsDisabled = fmt.Errorf("new subscriptions temporarily disabled") 86 + 42 87 func (s *Slurper) SubscribeToPds(ctx context.Context, host string, reg bool) error { 43 88 // TODO: for performance, lock on the hostname instead of global 44 89 s.lk.Lock() 45 90 defer s.lk.Unlock() 91 + if s.newSubsDisabled { 92 + return ErrNewSubsDisabled 93 + } 46 94 47 95 _, ok := s.active[host] 48 96 if ok { ··· 87 135 defer s.lk.Unlock() 88 136 89 137 var all []models.PDS 90 - if err := s.db.Find(&all).Error; err != nil { 138 + if err := s.db.Find(&all, "registered = true").Error; err != nil { 91 139 return err 92 140 } 93 141
+10
cmd/bigsky/main.go
··· 103 103 &cli.StringFlag{ 104 104 Name: "disk-blob-store", 105 105 }, 106 + &cli.StringFlag{ 107 + Name: "admin-key", 108 + EnvVars: []string{"BGS_ADMIN_KEY"}, 109 + }, 106 110 } 107 111 108 112 app.Action = func(cctx *cli.Context) error { ··· 199 203 bgs, err := bgs.NewBGS(db, ix, repoman, evtman, cachedidr, blobstore, !cctx.Bool("crawl-insecure-ws")) 200 204 if err != nil { 201 205 return err 206 + } 207 + 208 + if tok := cctx.String("admin-key"); tok != "" { 209 + if err := bgs.CreateAdminToken(tok); err != nil { 210 + return fmt.Errorf("failed to set up admin token: %w", err) 211 + } 202 212 } 203 213 204 214 // set up pprof endpoint
+4 -29
indexer/indexer.go
··· 5 5 "context" 6 6 "errors" 7 7 "fmt" 8 - "strings" 9 8 "time" 10 9 11 10 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 310 309 } 311 310 312 311 func (ix *Indexer) crawlAtUriRef(ctx context.Context, uri string) error { 313 - puri, err := parseAtUri(uri) 312 + puri, err := util.ParseAtUri(uri) 314 313 if err != nil { 315 314 return err 316 315 } else { ··· 538 537 } 539 538 540 539 func (ix *Indexer) GetPostOrMissing(ctx context.Context, uri string) (*models.FeedPost, error) { 541 - puri, err := parseAtUri(uri) 540 + puri, err := util.ParseAtUri(uri) 542 541 if err != nil { 543 542 return nil, err 544 543 } ··· 643 642 return ix.createMissingUserRecord(ctx, did) 644 643 } 645 644 646 - func (ix *Indexer) createMissingPostRecord(ctx context.Context, puri *parsedUri) (*models.FeedPost, error) { 645 + func (ix *Indexer) createMissingPostRecord(ctx context.Context, puri *util.ParsedUri) (*models.FeedPost, error) { 647 646 log.Warn("creating missing post record") 648 647 ai, err := ix.GetUserOrMissing(ctx, puri.Did) 649 648 if err != nil { ··· 793 792 } 794 793 795 794 func (ix *Indexer) GetPost(ctx context.Context, uri string) (*models.FeedPost, error) { 796 - puri, err := parseAtUri(uri) 795 + puri, err := util.ParseAtUri(uri) 797 796 if err != nil { 798 797 return nil, err 799 798 } ··· 804 803 } 805 804 806 805 return &post, nil 807 - } 808 - 809 - type parsedUri struct { 810 - Did string 811 - Collection string 812 - Rkey string 813 - } 814 - 815 - func parseAtUri(uri string) (*parsedUri, error) { 816 - if !strings.HasPrefix(uri, "at://") { 817 - return nil, fmt.Errorf("AT uris must be prefixed with 'at://'") 818 - } 819 - 820 - trimmed := strings.TrimPrefix(uri, "at://") 821 - parts := strings.Split(trimmed, "/") 822 - if len(parts) != 3 { 823 - return nil, fmt.Errorf("AT uris must have three parts: did, collection, tid") 824 - } 825 - 826 - return &parsedUri{ 827 - Did: parts[0], 828 - Collection: parts[1], 829 - Rkey: parts[2], 830 - }, nil 831 806 } 832 807 833 808 // TODO: since this function is the only place we depend on the repomanager, i wonder if this should be wired some other way?
+4 -1
labeler/service.go
··· 111 111 log.Infof("found labelmaker repo: %s", head) 112 112 } 113 113 114 - slurp := bgs.NewSlurper(db, s.handleBgsRepoEvent, useWss) 114 + slurp, err := bgs.NewSlurper(db, s.handleBgsRepoEvent, useWss) 115 + if err != nil { 116 + return nil, err 117 + } 115 118 s.bgsSlurper = slurp 116 119 117 120 go evtmgr.Run()
+30
util/uri.go
··· 1 + package util 2 + 3 + import ( 4 + "fmt" 5 + "strings" 6 + ) 7 + 8 + type ParsedUri struct { 9 + Did string 10 + Collection string 11 + Rkey string 12 + } 13 + 14 + func ParseAtUri(uri string) (*ParsedUri, error) { 15 + if !strings.HasPrefix(uri, "at://") { 16 + return nil, fmt.Errorf("AT uris must be prefixed with 'at://'") 17 + } 18 + 19 + trimmed := strings.TrimPrefix(uri, "at://") 20 + parts := strings.Split(trimmed, "/") 21 + if len(parts) != 3 { 22 + return nil, fmt.Errorf("AT uris must have three parts: did, collection, tid") 23 + } 24 + 25 + return &ParsedUri{ 26 + Did: parts[0], 27 + Collection: parts[1], 28 + Rkey: parts[2], 29 + }, nil 30 + }