123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // Copyright 2020 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package auth
-
- import (
- "context"
- "fmt"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
-
- "github.com/duo-labs/webauthn/webauthn"
- "xorm.io/xorm"
- )
-
- // ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
- type ErrWebAuthnCredentialNotExist struct {
- ID int64
- CredentialID []byte
- }
-
- func (err ErrWebAuthnCredentialNotExist) Error() string {
- if len(err.CredentialID) == 0 {
- return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID)
- }
- return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %x]", err.CredentialID)
- }
-
- // Unwrap unwraps this as a ErrNotExist err
- func (err ErrWebAuthnCredentialNotExist) Unwrap() error {
- return util.ErrNotExist
- }
-
- // IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist.
- func IsErrWebAuthnCredentialNotExist(err error) bool {
- _, ok := err.(ErrWebAuthnCredentialNotExist)
- return ok
- }
-
- // WebAuthnCredential represents the WebAuthn credential data for a public-key
- // credential conformant to WebAuthn Level 1
- type WebAuthnCredential struct {
- ID int64 `xorm:"pk autoincr"`
- Name string
- LowerName string `xorm:"unique(s)"`
- UserID int64 `xorm:"INDEX unique(s)"`
- CredentialID []byte `xorm:"INDEX VARBINARY(1024)"`
- PublicKey []byte
- AttestationType string
- AAGUID []byte
- SignCount uint32 `xorm:"BIGINT"`
- CloneWarning bool
- CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
- UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
- }
-
- func init() {
- db.RegisterModel(new(WebAuthnCredential))
- }
-
- // TableName returns a better table name for WebAuthnCredential
- func (cred WebAuthnCredential) TableName() string {
- return "webauthn_credential"
- }
-
- // UpdateSignCount will update the database value of SignCount
- func (cred *WebAuthnCredential) UpdateSignCount() error {
- return cred.updateSignCount(db.DefaultContext)
- }
-
- func (cred *WebAuthnCredential) updateSignCount(ctx context.Context) error {
- _, err := db.GetEngine(ctx).ID(cred.ID).Cols("sign_count").Update(cred)
- return err
- }
-
- // BeforeInsert will be invoked by XORM before updating a record
- func (cred *WebAuthnCredential) BeforeInsert() {
- cred.LowerName = strings.ToLower(cred.Name)
- }
-
- // BeforeUpdate will be invoked by XORM before updating a record
- func (cred *WebAuthnCredential) BeforeUpdate() {
- cred.LowerName = strings.ToLower(cred.Name)
- }
-
- // AfterLoad is invoked from XORM after setting the values of all fields of this object.
- func (cred *WebAuthnCredential) AfterLoad(session *xorm.Session) {
- cred.LowerName = strings.ToLower(cred.Name)
- }
-
- // WebAuthnCredentialList is a list of *WebAuthnCredential
- type WebAuthnCredentialList []*WebAuthnCredential
-
- // ToCredentials will convert all WebAuthnCredentials to webauthn.Credentials
- func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
- creds := make([]webauthn.Credential, 0, len(list))
- for _, cred := range list {
- creds = append(creds, webauthn.Credential{
- ID: cred.CredentialID,
- PublicKey: cred.PublicKey,
- AttestationType: cred.AttestationType,
- Authenticator: webauthn.Authenticator{
- AAGUID: cred.AAGUID,
- SignCount: cred.SignCount,
- CloneWarning: cred.CloneWarning,
- },
- })
- }
- return creds
- }
-
- // GetWebAuthnCredentialsByUID returns all WebAuthn credentials of the given user
- func GetWebAuthnCredentialsByUID(uid int64) (WebAuthnCredentialList, error) {
- return getWebAuthnCredentialsByUID(db.DefaultContext, uid)
- }
-
- func getWebAuthnCredentialsByUID(ctx context.Context, uid int64) (WebAuthnCredentialList, error) {
- creds := make(WebAuthnCredentialList, 0)
- return creds, db.GetEngine(ctx).Where("user_id = ?", uid).Find(&creds)
- }
-
- // ExistsWebAuthnCredentialsForUID returns if the given user has credentials
- func ExistsWebAuthnCredentialsForUID(uid int64) (bool, error) {
- return existsWebAuthnCredentialsByUID(db.DefaultContext, uid)
- }
-
- func existsWebAuthnCredentialsByUID(ctx context.Context, uid int64) (bool, error) {
- return db.GetEngine(ctx).Where("user_id = ?", uid).Exist(&WebAuthnCredential{})
- }
-
- // GetWebAuthnCredentialByName returns WebAuthn credential by id
- func GetWebAuthnCredentialByName(uid int64, name string) (*WebAuthnCredential, error) {
- return getWebAuthnCredentialByName(db.DefaultContext, uid, name)
- }
-
- func getWebAuthnCredentialByName(ctx context.Context, uid int64, name string) (*WebAuthnCredential, error) {
- cred := new(WebAuthnCredential)
- if found, err := db.GetEngine(ctx).Where("user_id = ? AND lower_name = ?", uid, strings.ToLower(name)).Get(cred); err != nil {
- return nil, err
- } else if !found {
- return nil, ErrWebAuthnCredentialNotExist{}
- }
- return cred, nil
- }
-
- // GetWebAuthnCredentialByID returns WebAuthn credential by id
- func GetWebAuthnCredentialByID(id int64) (*WebAuthnCredential, error) {
- return getWebAuthnCredentialByID(db.DefaultContext, id)
- }
-
- func getWebAuthnCredentialByID(ctx context.Context, id int64) (*WebAuthnCredential, error) {
- cred := new(WebAuthnCredential)
- if found, err := db.GetEngine(ctx).ID(id).Get(cred); err != nil {
- return nil, err
- } else if !found {
- return nil, ErrWebAuthnCredentialNotExist{ID: id}
- }
- return cred, nil
- }
-
- // HasWebAuthnRegistrationsByUID returns whether a given user has WebAuthn registrations
- func HasWebAuthnRegistrationsByUID(uid int64) (bool, error) {
- return db.GetEngine(db.DefaultContext).Where("user_id = ?", uid).Exist(&WebAuthnCredential{})
- }
-
- // GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID
- func GetWebAuthnCredentialByCredID(userID int64, credID []byte) (*WebAuthnCredential, error) {
- return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID)
- }
-
- func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID []byte) (*WebAuthnCredential, error) {
- cred := new(WebAuthnCredential)
- if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil {
- return nil, err
- } else if !found {
- return nil, ErrWebAuthnCredentialNotExist{CredentialID: credID}
- }
- return cred, nil
- }
-
- // CreateCredential will create a new WebAuthnCredential from the given Credential
- func CreateCredential(userID int64, name string, cred *webauthn.Credential) (*WebAuthnCredential, error) {
- return createCredential(db.DefaultContext, userID, name, cred)
- }
-
- func createCredential(ctx context.Context, userID int64, name string, cred *webauthn.Credential) (*WebAuthnCredential, error) {
- c := &WebAuthnCredential{
- UserID: userID,
- Name: name,
- CredentialID: cred.ID,
- PublicKey: cred.PublicKey,
- AttestationType: cred.AttestationType,
- AAGUID: cred.Authenticator.AAGUID,
- SignCount: cred.Authenticator.SignCount,
- CloneWarning: false,
- }
-
- if err := db.Insert(ctx, c); err != nil {
- return nil, err
- }
- return c, nil
- }
-
- // DeleteCredential will delete WebAuthnCredential
- func DeleteCredential(id, userID int64) (bool, error) {
- return deleteCredential(db.DefaultContext, id, userID)
- }
-
- func deleteCredential(ctx context.Context, id, userID int64) (bool, error) {
- had, err := db.GetEngine(ctx).ID(id).Where("user_id = ?", userID).Delete(&WebAuthnCredential{})
- return had > 0, err
- }
-
- // WebAuthnCredentials implementns the webauthn.User interface
- func WebAuthnCredentials(userID int64) ([]webauthn.Credential, error) {
- dbCreds, err := GetWebAuthnCredentialsByUID(userID)
- if err != nil {
- return nil, err
- }
-
- return dbCreds.ToCredentials(), nil
- }
|