Gitfed
bastien-mrq/gitfed / internal / store / revocations.go
package store

import (
	"time"

	bolt "go.etcd.io/bbolt"
)

// Certificates are stateless: once the CA signs one it stays cryptographically
// valid until it expires, even if the user is later deleted or the underlying
// key removed. This bucket is the revocation list that closes that window —
// SSH cert auth (internal/ssh) consults it on every handshake, so a revoked
// principal or key is refused immediately rather than only when the cert's
// short TTL runs out.
//
// Entries are keyed with a one-byte kind prefix so principals and key
// fingerprints share the bucket without colliding. The stored value is the
// revocation time (RFC3339), currently informational.
const (
	revokedPrincipalPrefix = "p:"
	revokedKeyPrefix       = "k:"
)

func (s *Store) revoke(key string) error {
	return s.db.Update(func(tx *bolt.Tx) error {
		return tx.Bucket(bucketRevoked).Put([]byte(key), []byte(time.Now().UTC().Format(time.RFC3339)))
	})
}

func (s *Store) unrevoke(key string) error {
	return s.db.Update(func(tx *bolt.Tx) error {
		return tx.Bucket(bucketRevoked).Delete([]byte(key))
	})
}

func (s *Store) isRevoked(key string) (bool, error) {
	var revoked bool
	err := s.db.View(func(tx *bolt.Tx) error {
		revoked = tx.Bucket(bucketRevoked).Get([]byte(key)) != nil
		return nil
	})
	return revoked, err
}

// RevokePrincipal blocks every certificate bearing this principal (e.g. when
// the local user is deleted). UnrevokePrincipal lifts it, so re-creating a
// user with the same name restores access.
func (s *Store) RevokePrincipal(principal string) error {
	return s.revoke(revokedPrincipalPrefix + principal)
}
func (s *Store) UnrevokePrincipal(principal string) error {
	return s.unrevoke(revokedPrincipalPrefix + principal)
}
func (s *Store) IsPrincipalRevoked(principal string) (bool, error) {
	return s.isRevoked(revokedPrincipalPrefix + principal)
}

// RevokeKey blocks any certificate issued for this public key fingerprint
// (e.g. when the key is removed from a user). UnrevokeKey lifts it.
func (s *Store) RevokeKey(fingerprint string) error { return s.revoke(revokedKeyPrefix + fingerprint) }
func (s *Store) UnrevokeKey(fingerprint string) error {
	return s.unrevoke(revokedKeyPrefix + fingerprint)
}
func (s *Store) IsKeyRevoked(fingerprint string) (bool, error) {
	return s.isRevoked(revokedKeyPrefix + fingerprint)
}