This commit is contained in:
Jabberwocky238
2026-07-17 05:55:31 -04:00
parent 67c07b3bcf
commit 68cda1972c
19 changed files with 897 additions and 897 deletions
+41 -41
View File
@@ -1,13 +1,13 @@
// SSH-transport (public key) access checks.
//
// An SSH key is matched at request time by fingerprint. AccessBySSHKey resolves
// a presented key to an SSHKey row and checks it against the repo's (or its
// namespace's) ACL -- the state-layer half of the IState.AccessBySSHKey
// contract. SSH has no anonymous path: the key must be registered, else
// ErrDenied.
//
// SSH keys are created via ACLUpsertSSHKeyOnNS/OnRepo (IStateMut); there is no
// separate AddSSHKey and no list/management read surface.
package state
@@ -22,17 +22,17 @@ import (
"simplegit/common"
)
// AccessBySSHKey authorizes key for perm on owner/name. SSH has no anonymous
// path: the key must be registered, else ErrDenied. A registered key may read a
// public repo without a grant; anything else needs an ACL grant (on the repo or
// its namespace) whose rank satisfies perm. Returns (true, nil) on allow;
// (false, ErrDenied) on an unknown key, unknown repo, or insufficient grant.
// skip-auth allows all.
func (s *LocalState) AccessBySSHKey(ctx context.Context, key ssh.PublicKey, owner, name string, perm common.Perm) (bool, error) {
r, err := s.findRepo(owner, name)
if err != nil {
if errors.Is(err, ErrRepoNotFound) {
return false, ErrDenied // never leak existence
return false, ErrDenied
}
return false, err
}
@@ -43,15 +43,15 @@ func (s *LocalState) AccessBySSHKey(ctx context.Context, key ssh.PublicKey, owne
return false, fmt.Errorf("lookup ssh key: %w", err)
}
if !has {
return false, ErrDenied // unknown key
return false, ErrDenied
}
// best-effort last-used stamp; must not block access
now := time.Now()
if _, err := s.engine.ID(sk.ID).Cols("last_used_at").Update(&SSHKey{LastUsedAt: &now}); err == nil {
sk.LastUsedAt = &now
}
// public read is open to any registered key
if perm == common.PermRead && !r.IsPrivate {
return true, nil
}
@@ -65,21 +65,21 @@ func (s *LocalState) AccessBySSHKey(ctx context.Context, key ssh.PublicKey, owne
return true, nil
}
// KnownSSHKey reports whether key is an admissible SSH credential: it must be
// registered AND carry at least one ACL grant. A registered key with no grant
// (an orphan -- e.g. its grant was ACLDelete'd, or its repo was DeleteRepo'd)
// is rejected here, at the PublicKeyCallback, so the SSH client falls through
// to another offered key that does have a grant.
//
// This mirrors Gitea's model where every registered key belongs to a user
// (principal): a key without a principal can never authorize anything, so it
// is not admitted. simplegit's namespace plays the principal role -- keys
// granted on a namespace are all equivalent for that namespace's repos, so
// which one the client offers first no longer matters (the multi-key footgun).
// SSH access therefore requires a grant; public repos stay anonymously
// readable over HTTP.
//
// skip-auth admits everything (no engine).
func (s *LocalState) KnownSSHKey(key ssh.PublicKey) (bool, error) {
var sk SSHKey
has, err := s.engine.Where("fingerprint = ?", fingerprint(key)).Get(&sk)
@@ -87,11 +87,11 @@ func (s *LocalState) KnownSSHKey(key ssh.PublicKey) (bool, error) {
return false, fmt.Errorf("lookup ssh key: %w", err)
}
if !has {
return false, ErrDenied // unknown key
return false, ErrDenied
}
// Admit only keys with at least one grant. A grant-less registered key can
// never pass AccessBySSHKey, and admitting it would block the client from
// trying a granted key.
hasGrant, err := s.keyHasGrant(sk.ID)
if err != nil {
return false, err
@@ -102,8 +102,8 @@ func (s *LocalState) KnownSSHKey(key ssh.PublicKey) (bool, error) {
return true, nil
}
// keyHasGrant reports whether SSH key credID has any ACL grant (repo or
// namespace), regardless of perm.
func (s *LocalState) keyHasGrant(credID int64) (bool, error) {
count, err := s.engine.Where("cred_type = ? AND cred_id = ?", CredTypeSSH, credID).Count(&ACL{})
if err != nil {