···99)10101111type Server struct {1212- ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:6555"`1313- DBPath string `env:"DB_PATH, default=spindle.db"`1414- Hostname string `env:"HOSTNAME, required"`1515- JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"`1616- PlcUrl string `env:"PLC_URL, default=https://plc.directory"`1717- Dev bool `env:"DEV, default=false"`1818- Owner string `env:"OWNER, required"`1919- Secrets Secrets `env:",prefix=SECRETS_"`2020- LogDir string `env:"LOG_DIR, default=/var/log/spindle"`2121- QueueSize int `env:"QUEUE_SIZE, default=100"`2222- MaxJobCount int `env:"MAX_JOB_COUNT, default=2"` // max number of jobs that run at a time1212+ ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:6555"`1313+ DBPath string `env:"DB_PATH, default=spindle.db"`1414+ Hostname string `env:"HOSTNAME, required"`1515+ JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"`1616+ PlcUrl string `env:"PLC_URL, default=https://plc.directory"`1717+ Dev bool `env:"DEV, default=false"`1818+ Owner syntax.DID `env:"OWNER, required"`1919+ Secrets Secrets `env:",prefix=SECRETS_"`2020+ LogDir string `env:"LOG_DIR, default=/var/log/spindle"`2121+ QueueSize int `env:"QUEUE_SIZE, default=100"`2222+ MaxJobCount int `env:"MAX_JOB_COUNT, default=2"` // max number of jobs that run at a time2323}24242525func (s Server) Did() syntax.DID {
+18-42
spindle/ingester.go
···991010 "tangled.org/core/api/tangled"1111 "tangled.org/core/eventconsumer"1212- "tangled.org/core/rbac"1312 "tangled.org/core/spindle/db"14131514 comatproto "github.com/bluesky-social/indigo/api/atproto"1616- "github.com/bluesky-social/indigo/atproto/identity"1715 "github.com/bluesky-social/indigo/atproto/syntax"1816 "github.com/bluesky-social/indigo/xrpc"1917 "github.com/bluesky-social/jetstream/pkg/models"2020- securejoin "github.com/cyphar/filepath-securejoin"2118)22192320type Ingester func(ctx context.Context, e *models.Event) error···7679 return fmt.Errorf("domain mismatch: %s != %s", record.Instance, domain)7780 }78817979- ok, err := s.e.IsSpindleInviteAllowed(did, rbacDomain)8282+ ok, err := s.e.IsSpindleMemberInviteAllowed(syntax.DID(did), s.cfg.Server.Did())8083 if err != nil || !ok {8184 l.Error("failed to add member", "did", did, "error", err)8285 return fmt.Errorf("failed to enforce permissions: %w", err)···9396 return fmt.Errorf("failed to add member: %w", err)9497 }95989696- if err := s.e.AddSpindleMember(rbacDomain, record.Subject); err != nil {9999+ if err := s.e.AddSpindleMember(syntax.DID(record.Subject), s.cfg.Server.Did()); err != nil {97100 l.Error("failed to add member", "error", err)98101 return fmt.Errorf("failed to add member: %w", err)99102 }···119122 return fmt.Errorf("failed to remove member: %w", err)120123 }121124122122- if err := s.e.RemoveSpindleMember(rbacDomain, record.Subject.String()); err != nil {125125+ if err := s.e.RemoveSpindleMember(record.Subject, s.cfg.Server.Did()); err != nil {123126 l.Error("failed to add member", "error", err)124127 return fmt.Errorf("failed to add member: %w", err)125128 }···173176 return fmt.Errorf("failed to add repo: %w", err)174177 }175178176176- didSlashRepo, err := securejoin.SecureJoin(did, record.Name)177177- if err != nil {178178- return err179179- }179179+ repoAt := syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", did, e.Commit.Collection, e.Commit.RKey))180180181181 // add repo to rbac182182- if err := s.e.AddRepo(did, rbac.ThisServer, didSlashRepo); err != nil {182182+ if err := s.e.AddRepo(repoAt); err != nil {183183 l.Error("failed to add repo to enforcer", "error", err)184184 return fmt.Errorf("failed to add repo: %w", err)185185 }186186187187 // add collaborators to rbac188188- owner, err := s.res.ResolveIdent(ctx, did)189189- if err != nil || owner.Handle.IsInvalidHandle() {190190- return err191191- }192192- if err := s.fetchAndAddCollaborators(ctx, owner, didSlashRepo); err != nil {188188+ if err := s.fetchAndAddCollaborators(ctx, repoAt); err != nil {193189 return err194190 }195191···224234 return nil225235 }226236227227- // TODO: get rid of this entirely228228- // resolve this aturi to extract the repo record229229- owner, err := s.res.ResolveIdent(ctx, repoAt.Authority().String())230230- if err != nil || owner.Handle.IsInvalidHandle() {231231- return fmt.Errorf("failed to resolve handle: %w", err)232232- }233233-234234- xrpcc := xrpc.Client{235235- Host: owner.PDSEndpoint(),236236- }237237-238238- resp, err := comatproto.RepoGetRecord(ctx, &xrpcc, "", tangled.RepoNSID, repoAt.Authority().String(), repoAt.RecordKey().String())239239- if err != nil {240240- return err241241- }242242-243243- repo := resp.Value.Val.(*tangled.Repo)244244- didSlashRepo, _ := securejoin.SecureJoin(owner.DID.String(), repo.Name)245245-246237 // check perms for this user247247- if ok, err := s.e.IsCollaboratorInviteAllowed(owner.DID.String(), rbac.ThisServer, didSlashRepo); !ok || err != nil {238238+ if ok, err := s.e.IsRepoCollaboratorInviteAllowed(syntax.DID(e.Did), repoAt); !ok || err != nil {248239 return fmt.Errorf("insufficient permissions: %w", err)249240 }250241251242 // add collaborator to rbac252252- if err := s.e.AddCollaborator(record.Subject, rbac.ThisServer, didSlashRepo); err != nil {243243+ if err := s.e.AddRepoCollaborator(syntax.DID(record.Subject), repoAt); err != nil {253244 l.Error("failed to add repo to enforcer", "error", err)254245 return fmt.Errorf("failed to add repo: %w", err)255246 }···240269 return nil241270}242271243243-func (s *Spindle) fetchAndAddCollaborators(ctx context.Context, owner *identity.Identity, didSlashRepo string) error {272272+func (s *Spindle) fetchAndAddCollaborators(ctx context.Context, repo syntax.ATURI) error {244273 l := s.l.With("component", "ingester", "handler", "fetchAndAddCollaborators")245274246275 l.Info("fetching and adding existing collaborators")247276248248- xrpcc := xrpc.Client{249249- Host: owner.PDSEndpoint(),277277+ ident, err := s.res.ResolveIdent(ctx, repo.Authority().String())278278+ if err != nil || ident.Handle.IsInvalidHandle() {279279+ return fmt.Errorf("failed to resolve handle: %w", err)250280 }251281252252- resp, err := comatproto.RepoListRecords(ctx, &xrpcc, tangled.RepoCollaboratorNSID, "", 50, owner.DID.String(), false)282282+ xrpcc := xrpc.Client{283283+ Host: ident.PDSEndpoint(),284284+ }285285+286286+ resp, err := comatproto.RepoListRecords(ctx, &xrpcc, tangled.RepoCollaboratorNSID, "", 50, ident.DID.String(), false)253287 if err != nil {254288 return err255289 }···266290 }267291 record := r.Value.Val.(*tangled.RepoCollaborator)268292269269- if err := s.e.AddCollaborator(record.Subject, rbac.ThisServer, didSlashRepo); err != nil {293293+ if err := s.e.AddRepoCollaborator(syntax.DID(record.Subject), syntax.ATURI(record.Repo)); err != nil {270294 l.Error("failed to add repo to enforcer", "error", err)271295 errors.Join(errs, fmt.Errorf("failed to add repo: %w", err))272296 }
+6-47
spindle/server.go
···1818 "tangled.org/core/jetstream"1919 "tangled.org/core/log"2020 "tangled.org/core/notifier"2121- "tangled.org/core/rbac"2121+ "tangled.org/core/rbac2"2222 "tangled.org/core/spindle/config"2323 "tangled.org/core/spindle/db"2424 "tangled.org/core/spindle/engine"···3333//go:embed motd3434var defaultMotd []byte35353636-const (3737- rbacDomain = "thisserver"3838-)3939-4036type Spindle struct {4137 jc *jetstream.JetstreamClient4238 db *db.DB4343- e *rbac.Enforcer3939+ e *rbac2.Enforcer4440 l *slog.Logger4541 n *notifier.Notifier4642 engs map[string]models.Engine···5862 return nil, fmt.Errorf("failed to setup db: %w", err)5963 }60646161- e, err := rbac.NewEnforcer(cfg.Server.DBPath)6565+ e, err := rbac2.NewEnforcer(cfg.Server.DBPath)6266 if err != nil {6367 return nil, fmt.Errorf("failed to setup rbac enforcer: %w", err)6468 }6565- e.E.EnableAutoSave(true)66696770 n := notifier.New()6871···102107 if err != nil {103108 return nil, fmt.Errorf("failed to setup jetstream client: %w", err)104109 }105105- jc.AddDid(cfg.Server.Owner)110110+ jc.AddDid(cfg.Server.Owner.String())106111107112 // Check if the spindle knows about any Dids;108113 dids, err := d.GetAllDids()···129134 motd: defaultMotd,130135 }131136132132- err = e.AddSpindle(rbacDomain)133133- if err != nil {134134- return nil, fmt.Errorf("failed to set rbac domain: %w", err)135135- }136136- err = spindle.configureOwner()137137+ err = e.SetSpindleOwner(spindle.cfg.Server.Owner, spindle.cfg.Server.Did())137138 if err != nil {138139 return nil, err139140 }···192201}193202194203// Enforcer returns the RBAC enforcer instance.195195-func (s *Spindle) Enforcer() *rbac.Enforcer {204204+func (s *Spindle) Enforcer() *rbac2.Enforcer {196205 return s.e197206}198207···390399 }391400392401 return nil393393-}394394-395395-func (s *Spindle) configureOwner() error {396396- cfgOwner := s.cfg.Server.Owner397397-398398- existing, err := s.e.GetSpindleUsersByRole("server:owner", rbacDomain)399399- if err != nil {400400- return err401401- }402402-403403- switch len(existing) {404404- case 0:405405- // no owner configured, continue406406- case 1:407407- // find existing owner408408- existingOwner := existing[0]409409-410410- // no ownership change, this is okay411411- if existingOwner == s.cfg.Server.Owner {412412- break413413- }414414-415415- // remove existing owner416416- err = s.e.RemoveSpindleOwner(rbacDomain, existingOwner)417417- if err != nil {418418- return nil419419- }420420- default:421421- return fmt.Errorf("more than one owner in DB, try deleting %q and starting over", s.cfg.Server.DBPath)422422- }423423-424424- return s.e.AddSpindleOwner(rbacDomain, cfgOwner)425402}