117 lines
4.8 KiB
Go
117 lines
4.8 KiB
Go
// Private-DB models for simplegit.
|
|
//
|
|
// This is a credential-centric access model: there is NO users table. The two
|
|
// things that can authenticate a git operation are an SSH public key or a PAT,
|
|
// and each is granted access to repos directly via the ACL table. This matches
|
|
// the Store interface (cmd/listener.go): AccessBySSHKey / AccessByToken resolve
|
|
// a credential and check it against a repo, with no user id in between.
|
|
//
|
|
// Anonymous access (no credential) is governed by Repo.IsPrivate: a public repo
|
|
// allows anonymous read; write always requires a credential. That special case
|
|
// lives in the store layer, not in ACL -- ACL only records explicit grants.
|
|
//
|
|
// xorm derives the schema from the struct tags via Sync2 (see InitEngine), so
|
|
// there is no hand-written DDL. GonicMapper is required so SSHKey -> "ssh_key"
|
|
// (the default SnakeMapper would produce "s_s_h_key"); this matches
|
|
// simpleconsole, whose raw-SQL joins rely on the same names.
|
|
package state
|
|
|
|
import (
|
|
"time"
|
|
|
|
"xorm.io/xorm"
|
|
"xorm.io/xorm/names"
|
|
)
|
|
|
|
// BaseModel is the common row shape: auto-increment PK plus xorm-managed
|
|
// created/updated/deleted timestamps (soft-delete via DeletedAt).
|
|
type BaseModel struct {
|
|
ID int64 `xorm:"pk autoincr" json:"id"`
|
|
CreatedAt time.Time `xorm:"created" json:"created_at"`
|
|
UpdatedAt time.Time `xorm:"updated" json:"updated_at"`
|
|
DeletedAt *time.Time `xorm:"deleted" json:"deleted_at,omitempty"`
|
|
}
|
|
|
|
// Repo is a registered repository. The on-disk bare repo lives under the git
|
|
// root; this row is the authority for its namespace/name and visibility.
|
|
type Repo struct {
|
|
BaseModel `xorm:"extends"`
|
|
NamespaceID int64 `xorm:"notnull unique(cred)" json:"namespace_id"`
|
|
Namespace string `xorm:"varchar(255) notnull index" json:"namespace"`
|
|
NameID int64 `xorm:"notnull unique(cred)" json:"name_id"`
|
|
Name string `xorm:"varchar(255) notnull" json:"name"`
|
|
IsPrivate bool `xorm:"default false" json:"is_private"` // public repos allow anonymous read
|
|
}
|
|
|
|
// SSHKey is a registered public key, resolved by fingerprint in AccessBySSHKey.
|
|
type SSHKey struct {
|
|
BaseModel `xorm:"extends"`
|
|
PublicKey string `xorm:"text notnull" json:"-"`
|
|
Fingerprint string `xorm:"varchar(255) unique notnull" json:"fingerprint"` // "SHA256:..."
|
|
KeyType string `xorm:"varchar(50) notnull" json:"key_type"` // ssh-ed25519, ssh-rsa, ...
|
|
Comment string `xorm:"varchar(255)" json:"comment,omitempty"`
|
|
LastUsedAt *time.Time `xorm:"" json:"last_used_at,omitempty"`
|
|
}
|
|
|
|
// PAT is an opaque HTTP credential. Only its sha256 hash is stored; the
|
|
// plaintext is returned once at creation. AccessByToken hashes the presented
|
|
// token and looks it up here by token_hash.
|
|
type PAT struct {
|
|
BaseModel `xorm:"extends"`
|
|
TokenHash string `xorm:"varchar(64) unique notnull" json:"-"` // hex(sha256(plaintext))
|
|
Prefix string `xorm:"varchar(20)" json:"prefix"` // first chars, for display only
|
|
Name string `xorm:"varchar(255)" json:"name"`
|
|
ExpiresAt *time.Time `xorm:"" json:"expires_at,omitempty"`
|
|
LastUsedAt *time.Time `xorm:"" json:"last_used_at,omitempty"`
|
|
}
|
|
|
|
// ACL grants a credential access to a repo. CredType selects which table CredID
|
|
// points at -- the foreign key is polymorphic by design, so it is NOT
|
|
// database-enforced: deleting a credential must clean up its ACL rows in the
|
|
// store layer. The unique(cred) group means one grant per credential per repo;
|
|
// raise it by updating perm, not by inserting a duplicate.
|
|
type ACL struct {
|
|
BaseModel `xorm:"extends"`
|
|
CredType int `xorm:"notnull unique(cred) index" json:"cred_type"` // CredTypePAT or CredTypeSSH
|
|
CredID int64 `xorm:"notnull unique(cred)" json:"cred_id"` // pats.id or ssh_keys.id (per CredType)
|
|
TargetType int `xorm:"notnull unique(cred) index" json:"target_type"` // TargetType
|
|
TargetID int64 `xorm:"notnull unique(cred)" json:"target_id"`
|
|
Perm string `xorm:"varchar(20) default 'read'" json:"perm"` // PermRead | PermWrite | PermAdmin
|
|
}
|
|
|
|
type CredType int
|
|
|
|
// Credential kinds for ACL.CredType.
|
|
const (
|
|
CredTypePAT CredType = 0 // HTTP transport (PAT)
|
|
CredTypeSSH CredType = 1 // SSH transport (public key)
|
|
)
|
|
|
|
type TargetType int
|
|
|
|
const (
|
|
TargetTypeRepo TargetType = 0
|
|
TargetTypeNS TargetType = 1
|
|
)
|
|
|
|
// InitEngine opens an xorm engine on driver/dsn, applies the GonicMapper (see
|
|
// package doc), and syncs all model tables. It mirrors simpleconsole's
|
|
// models.InitEngine so the two services share table/column naming.
|
|
func InitEngine(driverName, dataSourceName string) (*xorm.Engine, error) {
|
|
engine, err := xorm.NewEngine(driverName, dataSourceName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
engine.SetTableMapper(names.GonicMapper{})
|
|
engine.SetColumnMapper(names.GonicMapper{})
|
|
if err := engine.Sync2(
|
|
new(Repo),
|
|
new(SSHKey),
|
|
new(PAT),
|
|
new(ACL),
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return engine, nil
|
|
}
|