2018-02-26 01:32:27 +02:00
|
|
|
package authboss
|
|
|
|
|
2018-03-06 03:47:11 +02:00
|
|
|
import (
|
|
|
|
"fmt"
|
2018-03-09 04:39:51 +02:00
|
|
|
"strings"
|
2018-03-06 03:47:11 +02:00
|
|
|
"time"
|
2018-05-09 05:39:39 +02:00
|
|
|
|
2020-07-03 20:24:07 +02:00
|
|
|
"github.com/friendsofgo/errors"
|
2018-03-06 03:47:11 +02:00
|
|
|
)
|
2018-02-26 01:32:27 +02:00
|
|
|
|
|
|
|
// User has functions for each piece of data it requires.
|
|
|
|
// Data should not be persisted on each function call.
|
|
|
|
// User has a PID (primary ID) that is used on the site as
|
|
|
|
// a single unique identifier to any given user (very typically e-mail
|
|
|
|
// or username).
|
|
|
|
//
|
|
|
|
// User interfaces return no errors or bools to signal that a value was
|
|
|
|
// not present. Instead 0-value = null = not present, this puts the onus
|
|
|
|
// on Authboss code to check for this.
|
|
|
|
type User interface {
|
|
|
|
GetPID() (pid string)
|
|
|
|
PutPID(pid string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AuthableUser is identified by a password
|
|
|
|
type AuthableUser interface {
|
|
|
|
User
|
|
|
|
|
|
|
|
GetPassword() (password string)
|
|
|
|
PutPassword(password string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfirmableUser can be in a state of confirmed or not
|
|
|
|
type ConfirmableUser interface {
|
|
|
|
User
|
|
|
|
|
2018-02-27 17:14:30 +02:00
|
|
|
GetEmail() (email string)
|
2018-05-16 20:51:55 +02:00
|
|
|
GetConfirmed() (confirmed bool)
|
|
|
|
GetConfirmSelector() (selector string)
|
|
|
|
GetConfirmVerifier() (verifier string)
|
2018-02-26 01:32:27 +02:00
|
|
|
|
2018-02-27 17:14:30 +02:00
|
|
|
PutEmail(email string)
|
2018-05-16 20:51:55 +02:00
|
|
|
PutConfirmed(confirmed bool)
|
|
|
|
PutConfirmSelector(selector string)
|
|
|
|
PutConfirmVerifier(verifier string)
|
2018-02-26 01:32:27 +02:00
|
|
|
}
|
|
|
|
|
2018-02-28 07:20:55 +02:00
|
|
|
// LockableUser is a user that can be locked
|
|
|
|
type LockableUser interface {
|
|
|
|
User
|
|
|
|
|
|
|
|
GetAttemptCount() (attempts int)
|
|
|
|
GetLastAttempt() (last time.Time)
|
|
|
|
GetLocked() (locked time.Time)
|
|
|
|
|
|
|
|
PutAttemptCount(attempts int)
|
|
|
|
PutLastAttempt(last time.Time)
|
|
|
|
PutLocked(locked time.Time)
|
|
|
|
}
|
|
|
|
|
2018-03-06 03:47:11 +02:00
|
|
|
// RecoverableUser is a user that can be recovered via e-mail
|
|
|
|
type RecoverableUser interface {
|
|
|
|
AuthableUser
|
|
|
|
|
|
|
|
GetEmail() (email string)
|
2018-05-16 20:51:55 +02:00
|
|
|
GetRecoverSelector() (selector string)
|
|
|
|
GetRecoverVerifier() (verifier string)
|
2018-03-06 03:47:11 +02:00
|
|
|
GetRecoverExpiry() (expiry time.Time)
|
|
|
|
|
|
|
|
PutEmail(email string)
|
2018-05-16 20:51:55 +02:00
|
|
|
PutRecoverSelector(selector string)
|
|
|
|
PutRecoverVerifier(verifier string)
|
2018-03-06 03:47:11 +02:00
|
|
|
PutRecoverExpiry(expiry time.Time)
|
|
|
|
}
|
|
|
|
|
2023-09-27 09:38:29 +02:00
|
|
|
type RecoverableUserWithSecondaryEmails interface {
|
|
|
|
RecoverableUser
|
|
|
|
|
|
|
|
GetSecondaryEmails() (secondaryEmails []string)
|
|
|
|
}
|
|
|
|
|
2018-02-26 01:32:27 +02:00
|
|
|
// ArbitraryUser allows arbitrary data from the web form through. You should
|
|
|
|
// definitely only pull the keys you want from the map, since this is unfiltered
|
|
|
|
// input from a web request and is an attack vector.
|
|
|
|
type ArbitraryUser interface {
|
|
|
|
User
|
|
|
|
|
|
|
|
// GetArbitrary is used only to display the arbitrary data back to the user
|
|
|
|
// when the form is reset.
|
|
|
|
GetArbitrary() (arbitrary map[string]string)
|
|
|
|
// PutArbitrary allows arbitrary fields defined by the authboss library
|
|
|
|
// consumer to add fields to the user registration piece.
|
|
|
|
PutArbitrary(arbitrary map[string]string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// OAuth2User allows reading and writing values relating to OAuth2
|
2020-09-18 17:28:09 +02:00
|
|
|
// Also see MakeOAuthPID/ParseOAuthPID for helpers to fulfill the User
|
2018-03-09 04:39:51 +02:00
|
|
|
// part of the interface.
|
2018-02-26 01:32:27 +02:00
|
|
|
type OAuth2User interface {
|
|
|
|
User
|
|
|
|
|
|
|
|
// IsOAuth2User checks to see if a user was registered in the site as an
|
|
|
|
// oauth2 user.
|
|
|
|
IsOAuth2User() bool
|
|
|
|
|
2018-03-09 04:39:51 +02:00
|
|
|
GetOAuth2UID() (uid string)
|
|
|
|
GetOAuth2Provider() (provider string)
|
|
|
|
GetOAuth2AccessToken() (token string)
|
|
|
|
GetOAuth2RefreshToken() (refreshToken string)
|
|
|
|
GetOAuth2Expiry() (expiry time.Time)
|
|
|
|
|
|
|
|
PutOAuth2UID(uid string)
|
|
|
|
PutOAuth2Provider(provider string)
|
|
|
|
PutOAuth2AccessToken(token string)
|
|
|
|
PutOAuth2RefreshToken(refreshToken string)
|
|
|
|
PutOAuth2Expiry(expiry time.Time)
|
2018-02-26 01:32:27 +02:00
|
|
|
}
|
|
|
|
|
2018-03-06 03:47:11 +02:00
|
|
|
// MustBeAuthable forces an upgrade to an AuthableUser or panic.
|
2018-02-26 01:32:27 +02:00
|
|
|
func MustBeAuthable(u User) AuthableUser {
|
|
|
|
if au, ok := u.(AuthableUser); ok {
|
|
|
|
return au
|
|
|
|
}
|
2018-03-06 03:47:11 +02:00
|
|
|
panic(fmt.Sprintf("could not upgrade user to an authable user, type: %T", u))
|
2018-02-26 01:32:27 +02:00
|
|
|
}
|
|
|
|
|
2018-03-06 03:47:11 +02:00
|
|
|
// MustBeConfirmable forces an upgrade to a ConfirmableUser or panic.
|
2018-02-26 01:32:27 +02:00
|
|
|
func MustBeConfirmable(u User) ConfirmableUser {
|
|
|
|
if cu, ok := u.(ConfirmableUser); ok {
|
|
|
|
return cu
|
|
|
|
}
|
2018-03-06 03:47:11 +02:00
|
|
|
panic(fmt.Sprintf("could not upgrade user to a confirmable user, type: %T", u))
|
2018-02-26 01:32:27 +02:00
|
|
|
}
|
2018-02-28 07:20:55 +02:00
|
|
|
|
2018-03-06 03:47:11 +02:00
|
|
|
// MustBeLockable forces an upgrade to a LockableUser or panic.
|
2018-02-28 07:20:55 +02:00
|
|
|
func MustBeLockable(u User) LockableUser {
|
|
|
|
if lu, ok := u.(LockableUser); ok {
|
|
|
|
return lu
|
|
|
|
}
|
2018-03-06 03:47:11 +02:00
|
|
|
panic(fmt.Sprintf("could not upgrade user to a lockable user, given type: %T", u))
|
|
|
|
}
|
|
|
|
|
|
|
|
// MustBeRecoverable forces an upgrade to a RecoverableUser or panic.
|
|
|
|
func MustBeRecoverable(u User) RecoverableUser {
|
|
|
|
if lu, ok := u.(RecoverableUser); ok {
|
|
|
|
return lu
|
|
|
|
}
|
|
|
|
panic(fmt.Sprintf("could not upgrade user to a recoverable user, given type: %T", u))
|
2018-02-28 07:20:55 +02:00
|
|
|
}
|
2018-03-09 04:39:51 +02:00
|
|
|
|
2023-09-27 09:38:29 +02:00
|
|
|
func CanBeRecoverableUserWithSecondaryEmails(u User) (RecoverableUserWithSecondaryEmails, bool) {
|
|
|
|
if lu, ok := u.(RecoverableUserWithSecondaryEmails); ok {
|
|
|
|
return lu, true
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
2018-03-09 04:39:51 +02:00
|
|
|
// MustBeOAuthable forces an upgrade to an OAuth2User or panic.
|
|
|
|
func MustBeOAuthable(u User) OAuth2User {
|
|
|
|
if ou, ok := u.(OAuth2User); ok {
|
|
|
|
return ou
|
|
|
|
}
|
|
|
|
panic(fmt.Sprintf("could not upgrade user to an oauthable user, given type: %T", u))
|
|
|
|
}
|
|
|
|
|
|
|
|
// MakeOAuth2PID is used to create a pid for users that don't have
|
|
|
|
// an e-mail address or username in the normal system. This allows
|
|
|
|
// all the modules to continue to working as intended without having
|
|
|
|
// a true primary id. As well as not having to divide the regular and oauth
|
|
|
|
// stuff all down the middle.
|
|
|
|
func MakeOAuth2PID(provider, uid string) string {
|
|
|
|
return fmt.Sprintf("oauth2;;%s;;%s", provider, uid)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseOAuth2PID returns the uid and provider for a given OAuth2 pid
|
2018-05-09 05:39:39 +02:00
|
|
|
func ParseOAuth2PID(pid string) (provider, uid string, err error) {
|
2018-03-09 04:39:51 +02:00
|
|
|
splits := strings.Split(pid, ";;")
|
|
|
|
if len(splits) != 3 {
|
2018-05-09 05:39:39 +02:00
|
|
|
return "", "", errors.Errorf("failed to parse oauth2 pid, too many segments: %s", pid)
|
2018-03-09 04:39:51 +02:00
|
|
|
}
|
|
|
|
if splits[0] != "oauth2" {
|
2018-05-09 05:39:39 +02:00
|
|
|
return "", "", errors.Errorf("invalid oauth2 pid, did not start with oauth2: %s", pid)
|
|
|
|
}
|
|
|
|
|
|
|
|
return splits[1], splits[2], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseOAuth2PIDP returns the uid and provider for a given OAuth2 pid
|
|
|
|
func ParseOAuth2PIDP(pid string) (provider, uid string) {
|
|
|
|
var err error
|
|
|
|
provider, uid, err = ParseOAuth2PID(pid)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2018-03-09 04:39:51 +02:00
|
|
|
}
|
|
|
|
|
2018-05-09 05:39:39 +02:00
|
|
|
return provider, uid
|
2018-03-09 04:39:51 +02:00
|
|
|
}
|