package state import ( "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "time" "golang.org/x/crypto/bcrypt" "simplegit/common" ) // AccessByUserPswd authenticates a namespace-scoped User. The username is the // current Namespace.Name; the User row itself deliberately has no name. A // successful login owns that namespace and therefore authorizes its repos. func (s *LocalState) AccessByUserPswd(ctx context.Context, username, password, 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 } return false, err } if username == "" || password == "" || username != owner { return false, ErrDenied } var ns Namespace has, err := s.engine.Where("name = ?", username).Get(&ns) if err != nil { return false, fmt.Errorf("lookup user namespace: %w", err) } if !has || ns.ID != r.NamespaceID { return false, ErrDenied } if ns.PasswordHash == "" || bcrypt.CompareHashAndPassword([]byte(ns.PasswordHash), []byte(password)) != nil { return false, ErrInvalidCredentials } return true, nil } const patPrefix = "sgp_" func (s *LocalState) AccessByPAT(ctx context.Context, pat, 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 } return false, err } if perm == common.PermRead && !r.IsPrivate { return true, nil } if pat == "" { return false, ErrDenied } p, err := s.lookupPAT(pat) if err != nil { return false, err } ok, err := s.hasAccess(CredTypePAT, p.ID, r.ID, r.NamespaceID, perm) if err != nil { return false, err } if !ok { return false, ErrDenied } return true, nil } func (s *LocalState) lookupPAT(token string) (*PAT, error) { sum := sha256.Sum256([]byte(token)) var pat PAT has, err := s.engine.Where("token_hash = ?", hex.EncodeToString(sum[:])).Get(&pat) if err != nil { return nil, fmt.Errorf("lookup pat: %w", err) } if !has { return nil, ErrInvalidToken } if pat.ExpiresAt != nil && time.Now().After(*pat.ExpiresAt) { return nil, ErrInvalidToken } now := time.Now() if _, err := s.engine.ID(pat.ID).Cols("last_used_at").Update(&PAT{LastUsedAt: &now}); err == nil { pat.LastUsedAt = &now } return &pat, nil }