Documentation ¶
Index ¶
- Variables
- func CanonicalizeEmail(inp string) (string, error)
- func CheckVerificationToken(uw UserWrapper, expSecs int64, nonce, token string) error
- func DSTest(ctx context.Context, projectID string) error
- func DSTestWithDoneChan(ctx context.Context, projectID string) (<-chan struct{}, error)
- func GetSetting(ctx context.Context, client *datastore.Client, name string) ([]byte, error)
- func Idempotent(ctx context.Context, client *datastore.Client, key string) error
- func IsNoSession(err error) bool
- func LookupUser(ctx context.Context, client *datastore.Client, email string, uw UserWrapper) error
- func NewSetting(ctx context.Context, client *datastore.Client, name string, value []byte) error
- func NewUser(ctx context.Context, client *datastore.Client, email, pw string, ...) error
- func SessionHandler(client *datastore.Client, next http.Handler) http.Handler
- func SetSetting(ctx context.Context, client *datastore.Client, name string, value []byte) error
- func UpdatePW(ctx context.Context, client *datastore.Client, uw UserWrapper, pw string) error
- func UpdateUser(ctx context.Context, client *datastore.Client, email string, uw UserWrapper, ...) error
- func VerificationToken(uw UserWrapper) (expSecs int64, nonce, token string, err error)
- func VerifyUser(ctx context.Context, client *datastore.Client, uw UserWrapper, expSecs int64, ...) error
- type GCloudTasks
- type IdemKey
- type Session
- func ContextSession(ctx context.Context) *Session
- func GetSession(ctx context.Context, client *datastore.Client, req *http.Request) (*Session, error)
- func GetSessionByKey(ctx context.Context, client *datastore.Client, key *datastore.Key) (*Session, error)
- func NewSession(ctx context.Context, client *datastore.Client, userKey *datastore.Key) (*Session, error)
- func NewSessionWithDuration(ctx context.Context, client *datastore.Client, userKey *datastore.Key, ...) (*Session, error)
- func (s *Session) CSRFCheck(inp string) error
- func (s *Session) CSRFToken() (string, error)
- func (s *Session) Cancel(ctx context.Context, client *datastore.Client) error
- func (s *Session) GetUser(ctx context.Context, client *datastore.Client, uw UserWrapper) error
- func (s *Session) Key() *datastore.Key
- func (s *Session) SetCookie(w http.ResponseWriter)
- type Setting
- type TaskService
- type User
- type UserWrapper
Constants ¶
This section is empty.
Variables ¶
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") )
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).
var ErrCSRF = errors.New("CSRF check failed")
ErrCSRF is the error produced when an invalid CSRF token is presented to CSRFCheck.
var ErrIdempotency = errors.New("idempotency check failed")
ErrIdempotency is the error returned by Idempotent when the given key has been seen recently.
var ErrInactive = errors.New("inactive session")
ErrInactive means a session is inactive or expired.
var ErrUpdateConflict = errors.New("update conflict")
ErrUpdateConflict is the result of calling UpdateUser and losing a race with another concurrent caller.
Functions ¶
func CanonicalizeEmail ¶
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 ¶
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. When the given context is canceled, the emulator subprocess gets an interrupt signal, then a TERM signal, then a KILL signal, in an attempt to ensure it exits. The return value is a channel that gets closed when the emulator subprocess exits. 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. When the given context is canceled, the emulator subprocess gets an interrupt signal.
func DSTestWithDoneChan ¶ added in v1.7.0
DSTestWithDoneChan is the same as DSTest (qv) but it also returns a channel that closes when the emulator subprocess exits. The caller should block on this channel before exiting the main process.
func GetSetting ¶
GetSetting gets the value of a setting. If the setting does not exist, the result is datastore.ErrNoSuchEntity.
func Idempotent ¶
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 ¶
IsNoSession tells whether err is one of the unexceptional no-session errors (http.ErrNoCookie, datastore.ErrNoSuchEntity, and ErrInactive).
func LookupUser ¶
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 ¶
NewSetting sets the value for a setting key only if it doesn't already exist.
func NewUser ¶
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 ¶ added in v1.6.0
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 ¶
SetSetting creates or updates the value of a given setting.
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 ¶
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 ¶ added in v1.6.0
ContextSession returns the session attached to the context by SessionHandler, if any.
func GetSession ¶
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 ¶
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 ¶
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 ¶
Cancel cancels this session, setting its Active field to false. This is the way to effect a logout.
func (*Session) GetUser ¶
GetUser looks up the user associated with this session and places it in uw.
func (*Session) SetCookie ¶
func (s *Session) SetCookie(w http.ResponseWriter)
SetCookie adds a cookie for this session to an HTTP response.
type Setting ¶
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.
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.