<commit_before>package auth

import (
	"fmt"
	"time"

	"github.com/pkg/errors"
)

var errEmailVerifyHashExists = errors.New("DB: Email verify hash already exists")
var errInvalidEmailVerifyHash = errors.New("DB: Invalid verify code")
var errInvalidRenewTimeUTC = errors.New("DB: Invalid RenewTimeUTC")
var errInvalidSessionHash = errors.New("DB: Invalid SessionHash")
var errRememberMeSelectorExists = errors.New("DB: RememberMe selector already exists")
var errUserNotFound = errors.New("DB: User not found")
var errLoginNotFound = errors.New("DB: Login not found")
var errInvalidCredentials = errors.New("DB: Invalid Credentials")
var errSessionNotFound = errors.New("DB: Session not found")
var errSessionAlreadyExists = errors.New("DB: Session already exists")
var errRememberMeNotFound = errors.New("DB: RememberMe not found")
var errRememberMeNeedsRenew = errors.New("DB: RememberMe needs to be renewed")
var errRememberMeExpired = errors.New("DB: RememberMe is expired")
var errUserAlreadyExists = errors.New("DB: User already exists")

// Backender interface contains all the methods needed to read and write users, sessions and logins
type Backender interface {
	userBackender
	sessionBackender
	backendCloser
	Clone() Backender
}

type backendCloser interface {
	Close() error
}

// UserBackender interface holds methods for user management
type UserBackender interface {
	userBackender
	backendCloser
}

type userBackender interface {
	AddUser(email string, info map[string]interface{}) (string, error)
	AddUserFull(email, password string, info map[string]interface{}) (*User, error)
	GetUser(email string) (*User, error)
	UpdateUser(userID, password string, info map[string]interface{}) error
	UpdateInfo(userID string, info map[string]interface{}) error
	UpdatePassword(userID, newPassword string) error

	Login(email, password string) error
	LoginAndGetUser(email, password string) (*User, error)
	AddSecondaryEmail(userID, secondaryEmail string) error
	UpdatePrimaryEmail(userID, newPrimaryEmail string) error
}

// SessionBackender interface holds methods for session management
type SessionBackender interface {
	sessionBackender
	backendCloser
}

type sessionBackender interface {
	CreateEmailSession(email string, info map[string]interface{}, emailVerifyHash, csrfToken string) error
	GetEmailSession(verifyHash string) (*emailSession, error)
	UpdateEmailSession(verifyHash string, userID string) error
	DeleteEmailSession(verifyHash string) error

	CreateSession(userID, email string, info map[string]interface{}, sessionHash, csrfToken string, sessionRenewTimeUTC, sessionExpireTimeUTC time.Time) (*LoginSession, error)
	GetSession(sessionHash string) (*LoginSession, error)
	UpdateSession(sessionHash string, renewTimeUTC, expireTimeUTC time.Time) error
	DeleteSession(sessionHash string) error
	InvalidateSessions(email string) error

	CreateRememberMe(userID, email string, rememberMeSelector, rememberMeTokenHash string, renewTimeUTC, expireTimeUTC time.Time) (*rememberMeSession, error)
	GetRememberMe(selector string) (*rememberMeSession, error)
	UpdateRememberMe(selector string, renewTimeUTC time.Time) error
	DeleteRememberMe(selector string) error
}

type emailSession struct {
	UserID          string                 `bson:"userID"    json:"userID"`
	Email           string                 `bson:"email"     json:"email"`
	Info            map[string]interface{} `bson:"info"      json:"info"`
	EmailVerifyHash string                 `bson:"_id"       json:"emailVerifyHash"`
	CSRFToken       string                 `bson:"csrfToken" json:"csrfToken"`
}

type user struct {
	UserID            string
	PrimaryEmail      string
	PasswordHash      string
	Info              map[string]interface{}
	LockoutEndTimeUTC *time.Time
	AccessFailedCount int
}

// User is the struct which holds user information
type User struct {
	UserID string                 `json:"userID"`
	Email  string                 `json:"email"`
	Info   map[string]interface{} `json:"info"`
}

// LoginSession is the struct which holds session information
type LoginSession struct {
	UserID        string                 `bson:"userID"        json:"userID"`
	Email         string                 `bson:"email"         json:"email"`
	Info          map[string]interface{} `bson:"info"          json:"info"`
	SessionHash   string                 `bson:"_id"           json:"sessionHash"`
	CSRFToken     string                 `bson:"csrfToken"     json:"csrfToken"`
	RenewTimeUTC  time.Time              `bson:"renewTimeUTC"  json:"renewTimeUTC"`
	ExpireTimeUTC time.Time              `bson:"expireTimeUTC" json:"expireTimeUTC"`
}

// GetInfo will return the named info as an interface{}
func (l *LoginSession) GetInfo(name string) interface{} {
	if l == nil {
		return nil
	}
	return GetInfo(l.Info, name)
}

// GetInfoString will return the named info as a string
func (l *LoginSession) GetInfoString(name string) string {
	if l == nil {
		return ""
	}
	return GetInfoString(l.Info, name)
}

// GetInfoStrings will return the named info as an array of strings
func (l *LoginSession) GetInfoStrings(name string) []string {
	if l == nil {
		return nil
	}
	return GetInfoStrings(l.Info, name)
}

// GetInfo will return the named info as an interface{}
func GetInfo(info map[string]interface{}, name string) interface{} {
	if info == nil {
		return nil
	}<commit_msg>GetInfo* methods on User struct too<commit_after>package auth

import (
	"fmt"
	"time"

	"github.com/pkg/errors"
)

var errEmailVerifyHashExists = errors.New("DB: Email verify hash already exists")
var errInvalidEmailVerifyHash = errors.New("DB: Invalid verify code")
var errInvalidRenewTimeUTC = errors.New("DB: Invalid RenewTimeUTC")
var errInvalidSessionHash = errors.New("DB: Invalid SessionHash")
var errRememberMeSelectorExists = errors.New("DB: RememberMe selector already exists")
var errUserNotFound = errors.New("DB: User not found")
var errLoginNotFound = errors.New("DB: Login not found")
var errInvalidCredentials = errors.New("DB: Invalid Credentials")
var errSessionNotFound = errors.New("DB: Session not found")
var errSessionAlreadyExists = errors.New("DB: Session already exists")
var errRememberMeNotFound = errors.New("DB: RememberMe not found")
var errRememberMeNeedsRenew = errors.New("DB: RememberMe needs to be renewed")
var errRememberMeExpired = errors.New("DB: RememberMe is expired")
var errUserAlreadyExists = errors.New("DB: User already exists")

// Backender interface contains all the methods needed to read and write users, sessions and logins
type Backender interface {
	userBackender
	sessionBackender
	backendCloser
	Clone() Backender
}

type backendCloser interface {
	Close() error
}

// UserBackender interface holds methods for user management
type UserBackender interface {
	userBackender
	backendCloser
}

type userBackender interface {
	AddUser(email string, info map[string]interface{}) (string, error)
	AddUserFull(email, password string, info map[string]interface{}) (*User, error)
	GetUser(email string) (*User, error)
	UpdateUser(userID, password string, info map[string]interface{}) error
	UpdateInfo(userID string, info map[string]interface{}) error
	UpdatePassword(userID, newPassword string) error

	Login(email, password string) error
	LoginAndGetUser(email, password string) (*User, error)
	AddSecondaryEmail(userID, secondaryEmail string) error
	UpdatePrimaryEmail(userID, newPrimaryEmail string) error
}

// SessionBackender interface holds methods for session management
type SessionBackender interface {
	sessionBackender
	backendCloser
}

type sessionBackender interface {
	CreateEmailSession(email string, info map[string]interface{}, emailVerifyHash, csrfToken string) error
	GetEmailSession(verifyHash string) (*emailSession, error)
	UpdateEmailSession(verifyHash string, userID string) error
	DeleteEmailSession(verifyHash string) error

	CreateSession(userID, email string, info map[string]interface{}, sessionHash, csrfToken string, sessionRenewTimeUTC, sessionExpireTimeUTC time.Time) (*LoginSession, error)
	GetSession(sessionHash string) (*LoginSession, error)
	UpdateSession(sessionHash string, renewTimeUTC, expireTimeUTC time.Time) error
	DeleteSession(sessionHash string) error
	InvalidateSessions(email string) error

	CreateRememberMe(userID, email string, rememberMeSelector, rememberMeTokenHash string, renewTimeUTC, expireTimeUTC time.Time) (*rememberMeSession, error)
	GetRememberMe(selector string) (*rememberMeSession, error)
	UpdateRememberMe(selector string, renewTimeUTC time.Time) error
	DeleteRememberMe(selector string) error
}

type emailSession struct {
	UserID          string                 `bson:"userID"    json:"userID"`
	Email           string                 `bson:"email"     json:"email"`
	Info            map[string]interface{} `bson:"info"      json:"info"`
	EmailVerifyHash string                 `bson:"_id"       json:"emailVerifyHash"`
	CSRFToken       string                 `bson:"csrfToken" json:"csrfToken"`
}

type user struct {
	UserID            string
	PrimaryEmail      string
	PasswordHash      string
	Info              map[string]interface{}
	LockoutEndTimeUTC *time.Time
	AccessFailedCount int
}

// User is the struct which holds user information
type User struct {
	UserID string                 `json:"userID"`
	Email  string                 `json:"email"`
	Info   map[string]interface{} `json:"info"`
}

// LoginSession is the struct which holds session information
type LoginSession struct {
	UserID        string                 `bson:"userID"        json:"userID"`
	Email         string                 `bson:"email"         json:"email"`
	Info          map[string]interface{} `bson:"info"          json:"info"`
	SessionHash   string                 `bson:"_id"           json:"sessionHash"`
	CSRFToken     string                 `bson:"csrfToken"     json:"csrfToken"`
	RenewTimeUTC  time.Time              `bson:"renewTimeUTC"  json:"renewTimeUTC"`
	ExpireTimeUTC time.Time              `bson:"expireTimeUTC" json:"expireTimeUTC"`
}

// GetInfo will return the named info as an interface{}
func (l *LoginSession) GetInfo(name string) interface{} {
	if l == nil {
		return nil
	}
	return GetInfo(l.Info, name)
}

// GetInfoString will return the named info as a string
func (l *LoginSession) GetInfoString(name string) string {
	if l == nil {
		return ""
	}
	return GetInfoString(l.Info, name)
}

// GetInfoStrings will return the named info as an array of strings
func (l *LoginSession) GetInfoStrings(name string) []string {
	if l == nil {
		return nil
	}
	return GetInfoStrings(l.Info, name)
}

// GetInfo will return the named info as an interface{}
func (u *User) GetInfo(name string) interface{} {
	if u == nil {
		return nil
	}
	return GetInfo(u.Info, name)
}

// GetInfoString will return the named info as a string
func (u *User) GetInfoString(name string) string {
	if u == nil {
		return ""
	}
	return GetInfoString(u.Info, name)
}

// GetInfoStrings will return the named info as an array of strings
func (u *User) GetInfoStrings(name string) []string {
	if u == nil {
		return nil
	}
	return GetInfoStrings(u.Info, name)
}

// GetInfo will return the named info as an interface{}
func GetInfo(info map[string]interface{}, name string) interface{} {
	if info == nil {
		return nil
	}
{'fraction_non_alphanumeric': 0.09272227639393671, 'fraction_numerical': 0.0, 'mean_word_length': 4.27196261682243, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1936485', 'n_tokens_mistral': 3204, 'n_tokens_neox': 3155, 'n_words': 991}