this repo has no description
0
fork

Configure Feed

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

labeler: basic admin auth

+87 -7
+12
cmd/labelmaker/main.go
··· 92 92 EnvVars: []string{"LABELMAKER_REPO_HANDLE"}, 93 93 }, 94 94 &cli.StringFlag{ 95 + Name: "repo-password", 96 + Usage: "labelmaker repo password, used as admin password", 97 + Value: "admin", 98 + EnvVars: []string{"LABELMAKER_REPO_PASSWORD"}, 99 + }, 100 + &cli.StringFlag{ 95 101 Name: "signing-secret-key-jwk", 96 102 Usage: "signing key for labelmaker repo, in JWK serialization", 97 103 EnvVars: []string{"LABELMAKER_SIGNING_SECRET_KEY_JWK"}, ··· 191 197 useWss := !cctx.Bool("subscribe-insecure-ws") 192 198 repoDid := cctx.String("repo-did") 193 199 repoHandle := cctx.String("repo-handle") 200 + repoPassword := cctx.String("repo-password") 194 201 signingSecretKeyJwk := cctx.String("signing-secret-key-jwk") 195 202 bind := cctx.String("bind") 196 203 xrpcProxyURL := cctx.String("xrpc-proxy-url") ··· 198 205 microNSFWImgURL := cctx.String("micro-nsfw-img-url") 199 206 hiveAIToken := cctx.String("hiveai-api-token") 200 207 sqrlURL := cctx.String("sqrl-url") 208 + 209 + if repoPassword == "admin" { 210 + log.Warn("using insecure default admin password (ok for dev, not for deployment)") 211 + } 201 212 202 213 var serkey *did.PrivKey 203 214 if signingSecretKeyJwk != "" { ··· 215 226 repoUser := labeler.RepoConfig{ 216 227 Handle: repoHandle, 217 228 Did: repoDid, 229 + Password: repoPassword, 218 230 SigningKey: serkey, 219 231 UserId: 1, 220 232 }
+47 -2
labeler/service.go
··· 3 3 import ( 4 4 "bytes" 5 5 "context" 6 + "crypto/subtle" 6 7 "encoding/base64" 7 8 "fmt" 8 9 "io" ··· 55 56 type RepoConfig struct { 56 57 Handle string 57 58 Did string 59 + Password string 58 60 SigningKey *did.PrivKey 59 61 UserId util.Uid 60 62 } ··· 74 76 kmgr := indexer.NewKeyManager(didr, repoUser.SigningKey) 75 77 evtmgr := events.NewEventManager(events.NewMemPersister()) 76 78 repoman := repomgr.NewRepoManager(db, cs, kmgr) 79 + 80 + if repoUser.Password == "" || repoUser.Did == "" || repoUser.Handle == "" { 81 + return nil, fmt.Errorf("bad labeler repo config (empty string)") 82 + } 77 83 78 84 proxyURL, err := url.ParseRequestURI(xrpcProxyURL) 79 85 if err != nil { ··· 401 407 return nil 402 408 } 403 409 410 + // crude auth middleware to require "admin token" authentication on a subset of 411 + // routes. Does not implement the usual atproto JWT-based auth. "admin token" 412 + // auth is just HTTP Basic auth with username "admin" and a static password. 413 + // TODO: either transition to some other auth scheme, or review this more carefully 414 + func (s *Server) adminAuthMiddleware() echo.MiddlewareFunc { 415 + config := middleware.BasicAuthConfig{ 416 + Skipper: func(c echo.Context) bool { 417 + path := c.Request().URL.Path 418 + // all admin paths require auth 419 + if strings.HasPrefix(path, "/xrpc/com.atproto.admin.") { 420 + return false 421 + } 422 + // TODO: will need more complex auth on this endpoint eventually 423 + if strings.HasPrefix(path, "/xrpc/com.atproto.report.create") { 424 + return false 425 + } 426 + // everything else defaults open 427 + return true 428 + }, 429 + Validator: func(username, password string, c echo.Context) (bool, error) { 430 + // this is the default HTTP Basic validator from echo docs 431 + // "Be careful to use constant time comparison to prevent timing attacks" 432 + if subtle.ConstantTimeCompare([]byte(username), []byte("admin")) == 1 && 433 + subtle.ConstantTimeCompare([]byte(password), []byte(s.user.Password)) == 1 { 434 + return true, nil 435 + } 436 + log.Warnw("auth failed", "username", string(username)) 437 + return false, nil 438 + }, 439 + Realm: "AtprotoLabeler", 440 + } 441 + return middleware.BasicAuthWithConfig(config) 442 + } 443 + 404 444 func (s *Server) RunAPI(listen string) error { 405 445 e := echo.New() 406 446 s.echo = e ··· 408 448 e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ 409 449 Format: "method=${method} uri=${uri} status=${status} latency=${latency_human}\n", 410 450 })) 451 + e.Use(s.adminAuthMiddleware()) 411 452 412 453 e.HTTPErrorHandler = func(err error, ctx echo.Context) { 413 - fmt.Printf("Error at path=%s: %v\n", ctx.Path(), err) 414 - ctx.Response().WriteHeader(500) 454 + code := 500 455 + if he, ok := err.(*echo.HTTPError); ok { 456 + code = he.Code 457 + } 458 + log.Warnw("HTTP request error", "statusCode", code, "path", ctx.Path(), "err", err) 459 + ctx.Response().WriteHeader(code) 415 460 } 416 461 417 462 if err := s.RegisterHandlersComAtproto(e); err != nil {
+2 -1
labeler/service_test.go
··· 40 40 plcURL := "http://did-plc-test.dummy" 41 41 blobPdsURL := "http://pds-test.dummy" 42 42 xrpcProxyURL := "http://pds-test.dummy" 43 - xrpcProxyAdminPassword := "dummy-password" 43 + xrpcProxyAdminPassword := "xrpc-test-password" 44 44 repoUser := RepoConfig{ 45 45 Handle: "test.handle.dummy", 46 46 Did: "did:plc:testdummy", 47 + Password: "admin-test-password", 47 48 SigningKey: serkey, 48 49 UserId: 1, 49 50 }
+26 -4
testing/labelmaker_fakedata_test.go
··· 9 9 "testing" 10 10 "time" 11 11 12 + comatproto "github.com/bluesky-social/indigo/api/atproto" 12 13 label "github.com/bluesky-social/indigo/api/label" 13 14 "github.com/bluesky-social/indigo/carstore" 14 15 "github.com/bluesky-social/indigo/events" 15 16 "github.com/bluesky-social/indigo/labeler" 17 + "github.com/bluesky-social/indigo/util" 18 + "github.com/bluesky-social/indigo/xrpc" 16 19 17 20 "github.com/gorilla/websocket" 18 21 "github.com/stretchr/testify/assert" ··· 52 55 repoUser := labeler.RepoConfig{ 53 56 Handle: "test.handle.dummy", 54 57 Did: "did:plc:testdummy", 58 + Password: "test-admin-pass", 55 59 SigningKey: serkey, 56 60 UserId: 1, 57 61 } ··· 144 148 145 149 bob := p1.MustNewUser(t, "bob.tpds") 146 150 alice := p1.MustNewUser(t, "alice.tpds") 151 + fmt.Println("bob:", bob.DID()) 152 + fmt.Println("alice:", alice.DID()) 147 153 148 154 bp1 := bob.Post(t, "cats for cats") 149 155 ap1 := alice.Post(t, "no i like dogs") 150 - 151 156 _ = bp1 152 157 _ = ap1 153 158 154 - fmt.Println("bob:", bob.DID()) 155 - fmt.Println("alice:", alice.DID()) 159 + xrpcc := xrpc.Client{ 160 + Host: "http://localhost:7711", 161 + Client: util.TestingHTTPClient(), 162 + } 156 163 157 - // XXX: 164 + // no auth required 165 + queryOut, err := label.QueryLabels(ctx, &xrpcc, "", 20, []string{}, []string{"*"}) 166 + assert.NoError(err) 167 + assert.Equal(0, len(queryOut.Labels)) 168 + assert.Nil(queryOut.Cursor) 169 + 170 + // auth is required 171 + _, err = comatproto.AdminGetModerationReports(ctx, &xrpcc, "", 20, false, "") 172 + assert.Error(err) 173 + 174 + adminPassword := "test-admin-pass" 175 + xrpcc.AdminToken = &adminPassword 176 + _, err = comatproto.AdminGetModerationReports(ctx, &xrpcc, "", 20, false, "") 177 + assert.NoError(err) 178 + 179 + // TODO: many more tests 158 180 }