aesite

package module
v4.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2022 License: MIT Imports: 35 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrExpired is the result of checking an expired token.
	ErrExpired = errors.New("token expired")

	// ErrVerification is the result of checking an invalid token.
	ErrVerification = errors.New("token check failed")
)
View Source
var CookieName = "s"

CookieName is the name of the aesite session cookie.

View Source
var ErrAnonymous = errors.New("anonymous session")

ErrAnonymous is the result of calling Session.GetUser on an anonymous session (i.e., one with no UserKey set).

View Source
var ErrCSRF = errors.New("CSRF check failed")

ErrCSRF is the error produced when an invalid CSRF token is presented to CSRFCheck.

View Source
var ErrIdempotency = errors.New("idempotency check failed")

ErrIdempotency is the error returned by Idempotent when the given key has been seen recently.

View Source
var ErrInactive = errors.New("inactive session")

ErrInactive means a session is inactive or expired.

View Source
var ErrUpdateConflict = errors.New("update conflict")

ErrUpdateConflict is the result of calling UpdateUser and losing a race with another concurrent caller.

Functions

func CanonicalizeEmail

func CanonicalizeEmail(inp string) (string, error)

CanonicalizeEmail parses an e-mail address and returns it in a canonical form suitable for use as a lookup key.

func CheckVerificationToken

func CheckVerificationToken(uw UserWrapper, expSecs int64, nonce, token string) error

CheckVerificationToken checks a verification token for validity (including whether it has expired).

func DSTest

func DSTest(ctx context.Context, projectID string) ([]option.ClientOption, func() error, error)

DSTest starts the datastore emulator in a subprocess. It uses the gcloud binary, which must be found in PATH. (Further, the proper gcloud components must be installed; see https://cloud.google.com/datastore/docs/tools/datastore-emulator.) It gives the program two seconds to start, then sets this process's environment variables (again with gcloud) as needed for the datastore client library to use the emulator.

Returns a slice of option.ClientOptions that the caller should use in a call to datastore.NewClient, and a function that can be used to kill the emulator subprocess and return its Wait result.

(The ClientOptions add interceptors that reset a thirty-second timer on each call. The kill function waits until that timer expires before actually killing the emulator. This is necessary because the emulator may have buffered changes not yet written to disk. There does not appear to be a way to flush them explicitly, but waiting thirty seconds after the last change seems to do the trick.)

func GetSetting

func GetSetting(ctx context.Context, client *datastore.Client, name string) ([]byte, error)

GetSetting gets the value of a setting. If the setting does not exist, the result is datastore.ErrNoSuchEntity.

func Idempotent

func Idempotent(ctx context.Context, client *datastore.Client, key string) error

Idempotent stores a key (a string) in the datastore. A second attempt to store the same key will fail (with ErrIdempotent) for about an hour. This can be used to dedupe multiple identical task requests: every attempt calls Idempotent with the same string, and only the one that doesn't result in an error proceeds.

Calling Idempotent opportunistically deletes expired IdemKey records.

func IsNoSession

func IsNoSession(err error) bool

IsNoSession tells whether err is one of the unexceptional no-session errors (http.ErrNoCookie, datastore.ErrNoSuchEntity, and ErrInactive).

func LookupUser

func LookupUser(ctx context.Context, client *datastore.Client, email string, uw UserWrapper) error

LookupUser looks up a user by e-mail address and places the result in uw (which must be a pointer). The email argument is canonicalized with CanonicalizeEmail before the lookup.

func NewSetting

func NewSetting(ctx context.Context, client *datastore.Client, name string, value []byte) error

NewSetting sets the value for a setting key only if it doesn't already exist.

func NewUser

func NewUser(ctx context.Context, client *datastore.Client, email, pw string, uw UserWrapper) error

NewUser creates a new User object, places it in the given UserWrapper (which must be a pointer), and writes the whole thing to the datastore.

func SessionHandler

func SessionHandler(client *datastore.Client, next http.Handler) http.Handler

SessionHandler is HTTP middleware. It calls GetSession on an HTTP request. If one is found, the request's context is decorated with the session object before calling the next handler in the chain. That next handler can access the session by calling ContextSession.

func SetSetting

func SetSetting(ctx context.Context, client *datastore.Client, name string, value []byte) error

SetSetting creates or updates the value of a given setting.

func UpdatePW

func UpdatePW(ctx context.Context, client *datastore.Client, uw UserWrapper, pw string) error

UpdatePW sets a new password for the given user.

func UpdateUser

func UpdateUser(ctx context.Context, client *datastore.Client, email string, uw UserWrapper, f func(tx *datastore.Transaction) error) error

UpdateUser atomically updates a user.

To achieve this, UpdateUser uses optimistic locking. It starts a datastore transaction, then looks up the user and places it in uw (which must be a pointer). It next calls f to update the value in uw. After f runs (without error), UpdateUser fetches a new copy of the same user record to ensure that its UpdateCounter field hasn't changed. If it has, the transaction is rolled back and the error ErrUpdateConflict is returned. Otherwise, UpdateCounter is incremented and the transaction committed.

Note that this means f runs even if the user cannot ultimately be atomically updated. So f should not have side effects beyond what can be rolled back with tx.

func VerificationToken

func VerificationToken(uw UserWrapper) (expSecs int64, nonce, token string, err error)

VerificationToken generates a new verification token from a random nonce and an expiration time hashed with the user secret in uw. It returns the expTime, the nonce, and the token (all of which are needed by CheckVerificationToken).

func VerifyUser

func VerifyUser(ctx context.Context, client *datastore.Client, uw UserWrapper, expSecs int64, nonce, token string) error

VerifyUser checks a verification token for validity and sets the user's Verified flag to true. If the user is already verified, this is a no-op. The UserWrapper argument must be a pointer.

Types

type GCloudTasks

type GCloudTasks cloudtasks.Client

GCloudTasks is a Google cloudtasks client that satisfies the TaskService interface.

func NewGCloudTasks

func NewGCloudTasks(ctx context.Context, options ...option.ClientOption) (*GCloudTasks, error)

NewGCloudTasks produces a new GCloudTasks object.

func (*GCloudTasks) Enqueue

func (t *GCloudTasks) Enqueue(ctx context.Context, queue, taskName, url string, when time.Time) error

EnqueueTask enqueues a task with the given name on the given queue, which at the given time will GET the given URL.

func (*GCloudTasks) IsQueueEmpty

func (t *GCloudTasks) IsQueueEmpty(ctx context.Context, queue string) (bool, error)

IsQueueEmpty tells whether the queue with the given name is empty.

type IdemKey

type IdemKey struct {
	// Key is a caller-supplied string.
	Key string

	// Exp is the expiration time of this key.
	Exp time.Time
}

IdemKey is the type of an idempotency key in the datastore.

type Session

type Session struct {
	// ID is a unique random identifier for the session.
	ID int64 `json:"id"`

	// UserKey is the Google Cloud Datastore key for the User entity associated with this session.
	UserKey *datastore.Key `json:"user_key"`

	// CSRFKey is a unique random bytestring that can be used for CSRF protection.
	// See Session.CSRFToken and Session.CSRFCheck.
	CSRFKey []byte `json:"-"`

	// Active is true until Session.Cancel is called.
	Active bool `json:"active"`

	// Exp is the expiration time for this session.
	// This defaults to 30 days after the session was created.
	Exp time.Time `json:"exp"`
}

Session is the type of a user login session. It is stored as an entity of kind "Session" in Google Cloud Datastore.

func ContextSession

func ContextSession(ctx context.Context) *Session

ContextSession returns the session attached to the context by SessionHandler, if any.

func GetSession

func GetSession(ctx context.Context, client *datastore.Client, req *http.Request) (*Session, error)

GetSession checks for a session cookie in a given HTTP request and gets the session from the datastore. The cookie must have been handed out in an earlier HTTP response via Session.SetCookie. If there is no cookie in the HTTP request, the resulting error is http.ErrNoCookie. If the session is not present in the datastore, the resulting error is datastore.ErrNoSuchEntity. If the session is present but expired or inactive, the resulting error is ErrInactive. You can use IsNoSession to test whether the error is any one of those.

func GetSessionByKey

func GetSessionByKey(ctx context.Context, client *datastore.Client, key *datastore.Key) (*Session, error)

GetSessionByKey gets the session with the given key. If the session is not present in the datastore, the resulting error is datastore.ErrNoSuchEntity. If the session is present but expired or inactive, the resulting error is ErrInactive.

func NewSession

func NewSession(ctx context.Context, client *datastore.Client, userKey *datastore.Key) (*Session, error)

NewSession creates a new session for the given user and stores it in the datastore.

func NewSessionWithDuration

func NewSessionWithDuration(ctx context.Context, client *datastore.Client, userKey *datastore.Key, dur time.Duration) (*Session, error)

NewSessionWithDuration creates a new session for the given user that expires after the given duration, and stores it in the datastore.

func (*Session) CSRFCheck

func (s *Session) CSRFCheck(inp string) error

CSRFCheck checks a CSRF token for validity against this session. The token should have been produced with Session.CSRFToken. If the token is invalid, the result is CSRFErr. Other errors are possible too. A return value of nil means the token is valid.

func (*Session) CSRFToken

func (s *Session) CSRFToken() (string, error)

CSRFToken generates a new token containing a random nonce hashed with this session's CSRF key. It can be used to protect against CSRF attacks. Resources served by the application (e.g. HTML pages) should include a CSRF token. State-changing requests to the application that rely on a Session for authentication should require the caller to supply a valid CSRF token. Validity can be checked with Session.CSRFCheck. For more on this topic see https://en.wikipedia.org/wiki/Cross-site_request_forgery.

func (*Session) Cancel

func (s *Session) Cancel(ctx context.Context, client *datastore.Client) error

Cancel cancels this session, setting its Active field to false. This is the way to effect a logout.

func (*Session) GetUser

func (s *Session) GetUser(ctx context.Context, client *datastore.Client, uw UserWrapper) error

GetUser looks up the user associated with this session and places it in uw.

func (*Session) Key

func (s *Session) Key() *datastore.Key

Key returns this session's datastore key.

func (*Session) SetCookie

func (s *Session) SetCookie(w http.ResponseWriter)

SetCookie adds a cookie for this session to an HTTP response.

type Setting

type Setting struct {
	Name  string
	Value []byte
}

Setting is a name-value pair stored as an entity of kind "Setting" in Google Cloud Datastore.

type TaskService

type TaskService interface {
	IsQueueEmpty(ctx context.Context, queue string) (bool, error)
	Enqueue(ctx context.Context, queue, taskName, url string, when time.Time) error
}

TaskService is a minimal interface to a cloudtasks service. It is intended to permit simple testing, and to make it possible to swap out a cloudtasks service for a local tasks service TODO: local tasks service not yet implemented.

type User

type User struct {
	// Email is the user's e-mail address. It is used as a unique key for the User record.
	Email string `json:"email"`

	// PWHash is the scrypt hash (salted with Salt) of the user's password.
	PWHash []byte `json:"-"` // scrypt

	// Salt is a random byte string used as scrypt salt for PWHash.
	Salt []byte `json:"-"`

	// Verified is false until User.Verify is called, setting it to true.
	// When signing up new users,
	// Verify should be the result of navigating to an e-mail-confirmation link.
	Verified bool `json:"verified"`

	// Secret is a random bytestring used for calculating verification tokens.
	// Applications must take care not to let this value leak.
	Secret []byte `json:"-"`

	// UpdateCounter is incremented at the end of a successful call to UpdateUser.
	// It should not be used for any other purpose.
	UpdateCounter int64 `json:"-"`
}

User is the type of a user identified by an e-mail address.

func (*User) CheckPW

func (u *User) CheckPW(pw string) bool

CheckPW tests a password input for validity against this user's Salt and PWHash.

func (*User) CheckToken

func (u *User) CheckToken(wt io.WriterTo, token string) error

func (*User) GetUser

func (u *User) GetUser() *User

GetUser implements UserWrapper.GetUser.

func (*User) Key

func (u *User) Key() *datastore.Key

Key returns the datastore key for this user.

func (*User) SecureToken

func (u *User) SecureToken(wt io.WriterTo) (string, error)

func (*User) SetUser

func (u *User) SetUser(u2 *User)

SetUser implements UserWrapper.SetUser.

type UserWrapper

type UserWrapper interface {
	// GetUser unwraps the UserWrapper object to produce a *User.
	GetUser() *User

	// SetUser sets the *User of a UserWrapper.
	SetUser(*User)
}

UserWrapper is the type of an object with kind "User" that gets written to and read from the datastore. It can be used by callers to wrap application-specific user data around the User type defined here. The caller's implementation of UserWrapper must be able to convert to and from aesite.User. Note: aesite.User can act as its own (trivial) UserWrapper.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL