s
This commit is contained in:
+116
@@ -0,0 +1,116 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user