1
0
mirror of https://github.com/volatiletech/authboss.git synced 2024-11-24 08:42:17 +02:00
authboss/storer.go
Aaron L 24fc6196c7 Introduce new type of client storage
- This addresses the problem of having to update multiple times during
  one request. It's hard to have a nice interface especially with JWT
  because you always end up having to decode the request, encode new
  response, write header, then a second write to it comes, and where do
  you grab the value from? Often you don't have access to the response
  as a "read" structure. So we store it as events instead, and play
  those events against the original data right before the response is
  written to set the headers.
2017-02-24 16:45:47 -08:00

130 lines
3.9 KiB
Go

package authboss
import (
"bytes"
"context"
"time"
"github.com/pkg/errors"
)
// Data store constants for attribute names.
const (
StoreEmail = "email"
StoreUsername = "username"
StorePassword = "password"
)
// Data store constants for OAuth2 attribute names.
const (
StoreOAuth2UID = "oauth2_uid"
StoreOAuth2Provider = "oauth2_provider"
StoreOAuth2Token = "oauth2_token"
StoreOAuth2Refresh = "oauth2_refresh"
StoreOAuth2Expiry = "oauth2_expiry"
)
var (
// ErrUserNotFound should be returned from Get when the record is not found.
ErrUserNotFound = errors.New("user not found")
// ErrTokenNotFound should be returned from UseToken when the record is not found.
ErrTokenNotFound = errors.New("token not found")
// ErrUserFound should be returned from Create when the primaryID of the record is found.
ErrUserFound = errors.New("user found")
)
// StoreLoader represents the data store that's capable of loading users
// and giving them a context with which to store themselves.
type StoreLoader interface {
// Load will be passed the PrimaryID and return pre-loaded storer (meaning
// Storer.Load will not be called)
Load(ctx context.Context, key string) (Storer, error)
}
// Storer represents a user that also knows how to put himself into the db.
// It has functions for each piece of data it requires.
// Note that you should only persist data once Save() has been called.
type Storer interface {
PutEmail(ctx context.Context, email string) error
PutUsername(ctx context.Context, username string) error
PutPassword(ctx context.Context, password string) error
GetEmail(ctx context.Context) (email string, err error)
GetUsername(ctx context.Context) (username string, err error)
GetPassword(ctx context.Context) (password string, err error)
// Save the state.
Save(ctx context.Context) error
// Load the state based on the properties that have been given (typically
// an e-mail/username).
Load(ctx context.Context) error
}
// TODO(aarondl): Document & move to Register module
// ArbitraryStorer 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 ArbitraryStorer interface {
Storer
// PutArbitrary allows arbitrary fields defined by the authboss library
// consumer to add fields to the user registration piece.
PutArbitrary(ctx context.Context, arbitrary map[string]string) error
// GetArbitrary is used only to display the arbitrary data back to the user
// when the form is reset.
GetArbitrary(ctx context.Context) (arbitrary map[string]string, err error)
}
// OAuth2Storer allows reading and writing values relating to OAuth2
type OAuth2Storer interface {
Storer
IsOAuth2User(ctx context.Context) (bool, error)
PutUID(ctx context.Context, uid string) error
PutProvider(ctx context.Context, provider string) error
PutToken(ctx context.Context, token string) error
PutRefreshToken(ctx context.Context, refreshToken string) error
PutExpiry(ctx context.Context, expiry time.Duration) error
GetUID(ctx context.Context) (uid string, err error)
GetProvider(ctx context.Context) (provider string, err error)
GetToken(ctx context.Context) (token string, err error)
GetRefreshToken(ctx context.Context) (refreshToken string, err error)
GetExpiry(ctx context.Context) (expiry time.Duration, err error)
}
func camelToUnder(in string) string {
out := bytes.Buffer{}
for i := 0; i < len(in); i++ {
chr := in[i]
if chr >= 'A' && chr <= 'Z' {
if i > 0 {
out.WriteByte('_')
}
out.WriteByte(chr + 'a' - 'A')
} else {
out.WriteByte(chr)
}
}
return out.String()
}
func underToCamel(in string) string {
out := bytes.Buffer{}
for i := 0; i < len(in); i++ {
chr := in[i]
if first := i == 0; first || chr == '_' {
if !first {
i++
}
out.WriteByte(in[i] - 'a' + 'A')
} else {
out.WriteByte(chr)
}
}
return out.String()
}