// Shared access-check primitives and errors. // // state defines its own error sentinels (ErrDenied / ErrInvalidToken / // ErrRepoNotFound) and stays free of any gitrpc dependency -- the cmd layer // maps these to HTTP/SSH outcomes. hasAccess is the single ACL-rank check // shared by the SSH and PAT access paths. package state import ( "errors" "fmt" "simplegit/common" ) var ( // ErrDenied means the caller is not permitted: an anonymous caller needing // a grant, or an authenticated caller whose ACL grant is insufficient. ErrDenied = errors.New("access denied") // ErrInvalidToken means a PAT was presented but could not be matched (unknown // hash) or has expired. Distinct from anonymous (no PAT at all). ErrInvalidToken = errors.New("invalid or expired token") // ErrRepoNotFound means no DB row for the (owner, name). Surfaced to callers // of Open/RepoPath; access paths map it to ErrDenied so existence is never // leaked. ErrRepoNotFound = errors.New("repository not found") // ErrNamespaceNotFound means no repo exists in the namespace yet, so its // shared NamespaceID cannot be resolved. CreateRepo treats this as "mint a // new NamespaceID"; ACLUpsertOnNS surfaces it (grant before any repo exists). ErrNamespaceNotFound = errors.New("namespace does not exist") ) // hasAccess reports whether the credential (credType, credID) holds an ACL grant // satisfying perm on the repo (repoID) or its namespace (nsID). ACL targets are // polymorphic -- TargetTypeRepo or TargetTypeNS -- so a grant on either applies, // and the best-ranked matching grant wins. No grant means false (the public-read // shortcut is handled by the caller before this is called). func (s *LocalState) hasAccess(credType CredType, credID, repoID, nsID int64, perm common.Perm) (bool, error) { var acls []ACL err := s.engine.Where("cred_type = ? AND cred_id = ? AND ((target_type = ? AND target_id = ?) OR (target_type = ? AND target_id = ?))", credType, credID, TargetTypeRepo, repoID, TargetTypeNS, nsID).Find(&acls) if err != nil { return false, fmt.Errorf("lookup acl: %w", err) } best := 0 for _, a := range acls { if r := common.Perm(a.Perm).PermRank(); r > best { best = r } } return best >= perm.PermRank(), nil }