func loadEntities(ctx context.Context, w http.ResponseWriter, r *http.Request) { if err := dsimport.Process(ctx, r.Body); err != nil { rest.WriteJSON(w, err) } else { rest.WriteJSON(w, &rest.NoContent) } }
func getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request) { conf := config.Config{} if err := config.Get(ctx, &conf); err != nil { rest.WriteJSON(w, err) } else { rest.WriteJSON(w, &conf) } }
func getAccount(ctx context.Context, w http.ResponseWriter, r *http.Request) { accountId := rest.Param(ctx, "id") var acct account.Account if email, err := base64.RawURLEncoding.DecodeString(accountId); err != nil { rest.WriteJSON(w, err) } else if err := account.Get(ctx, string(email), &acct); err != nil { rest.WriteJSON(w, err) } else { rest.WriteJSON(w, &acct) } }
func newAccount(ctx context.Context, w http.ResponseWriter, r *http.Request) { var acctReq accountCreationRequest if err := rest.ReadJSON(r, &acctReq); err != nil { rest.WriteJSON(w, err) } else if newAcct, err := account.New(ctx, acctReq.Email, acctReq.Password); err != nil { rest.WriteJSON(w, err) } else { newAccountURL, _ := r.URL.Parse(newAcct.Key(ctx).StringID()) w.Header().Set("Location", newAccountURL.String()) rest.WriteJSON(w, rest.CreatedResponse(newAcct)) } }
func changeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request) { conf := config.Config{} if err := config.Get(ctx, &conf); err != nil { rest.WriteJSON(w, err) } else if err := rest.ReadJSON(r, &conf); err != nil { rest.WriteJSON(w, err) } else if err := config.Save(ctx, &conf); err != nil { rest.WriteJSON(w, err) } else { rest.WriteJSON(w, &conf) } }
// Middleware sets up the request context so account information can be // retrieved with auth.GetAccount(ctx). It panics if config.Get(ctx) fails. func Middleware(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context { var conf config.Global if err := config.Get(ctx, &conf); err != nil { panic("Could not get AuthSecret: " + err.Error()) } else if claimSet, err := Decode([]byte(r.Header.Get("Authorization")), []byte(conf.AuthSecret)); err != nil { ctx = context.WithValue(ctx, internal.ClaimSetContextKey, err) return context.WithValue(ctx, internal.AuthContextKey, err) } else if claimSet == SuperClaimSet { ctx = context.WithValue(ctx, internal.ClaimSetContextKey, SuperClaimSet) return context.WithValue(ctx, internal.AuthContextKey, &account.Super) } else if claimSet == NobodyClaimSet { ctx = context.WithValue(ctx, internal.ClaimSetContextKey, NobodyClaimSet) return context.WithValue(ctx, internal.AuthContextKey, &account.Nobody) } else { var acct account.Account if err := account.Get(ctx, claimSet.Sub, &acct); err != nil { rest.WriteJSON(w, &rest.Response{ Code: http.StatusUnauthorized, Body: &rest.Message{"Could not retrieve account with key " + claimSet.Sub + ": " + err.Error()}, }) return nil } else { ctx = context.WithValue(ctx, internal.ClaimSetContextKey, claimSet) return context.WithValue(ctx, internal.AuthContextKey, &acct) } } }
func getJwt(ctx context.Context, w http.ResponseWriter, r *http.Request) { var acct account.Account var email string var err error conf := config.Global{} if err := config.Get(ctx, &conf); err != nil { rest.WriteJSON(w, err) return } // read the account ID if emailBytes, err := base64.RawURLEncoding.DecodeString(rest.Param(ctx, "id")); err != nil { rest.WriteJSON(w, err) return } else { email = string(emailBytes) } if err = account.Get(ctx, email, &acct); err != nil { rest.WriteJSON(w, err) return } // All set. Generate the JWT. jwt, err := auth.Encode(&jws.ClaimSet{ Sub: acct.Email, Scope: strings.Join(acct.Roles, ","), Exp: time.Now().AddDate(1, 0, 0).Unix(), PrivateClaims: map[string]interface{}{ "n": "DEV USER", }, }, []byte(conf.AuthSecret)) if err != nil { rest.WriteJSON(w, errors.New(http.StatusInternalServerError, "Could not generate JWT")) return } else { jwtString := string(jwt) rest.WriteJSON(w, &jwtString) return } }
func changeAccount(ctx context.Context, w http.ResponseWriter, r *http.Request) { var acct account.Account var resp rest.Response resp.Body = &acct var email string var err error // read the account ID if emailBytes, err := base64.RawURLEncoding.DecodeString(rest.Param(ctx, "id")); err != nil { rest.WriteJSON(w, err) return } else { email = string(emailBytes) } // read the account and merge it with the request body if err = account.Get(ctx, email, &acct); err != nil { rest.WriteJSON(w, err) return } else if err = rest.ReadJSON(r, &acct); err != nil { rest.WriteJSON(w, err) return } // is the email address changing? if email != acct.Email { // run the change, then reread the account if err = account.ChangeEmail(ctx, email, acct.Email); err != nil { rest.WriteJSON(w, err) return } email = acct.Email if err = account.Get(ctx, email, &acct); err != nil { rest.WriteJSON(w, err) return } // point to the new URL on response newAccountURL, _ := r.URL.Parse("..") newAccountURL, _ = newAccountURL.Parse(base64.RawURLEncoding.EncodeToString([]byte(acct.Email))) w.Header().Set("Location", newAccountURL.String()) resp.Code = http.StatusMovedPermanently } // save the remaining changes to the account if err = account.Save(ctx, &acct); err != nil { rest.WriteJSON(w, err) return } rest.WriteJSON(w, &resp) }
func changeAccountPassword(ctx context.Context, w http.ResponseWriter, r *http.Request) { var acct account.Account var email string var newPassword string // read the account ID if emailBytes, err := base64.RawURLEncoding.DecodeString(rest.Param(ctx, "id")); err != nil { rest.WriteJSON(w, err) return } else { email = string(emailBytes) } if err := rest.ReadJSON(r, &newPassword); err != nil { rest.WriteJSON(w, err) } else if err := account.Get(ctx, email, &acct); err != nil { rest.WriteJSON(w, err) } else if err := acct.SetPassword(newPassword); err != nil { rest.WriteJSON(w, err) } else if err := account.Save(ctx, &acct); err != nil { rest.WriteJSON(w, err) } else { rest.WriteJSON(w, &rest.NoContent) } }
// Then produces a kami.Handler that wraps another handler with authentication magic. // If every AuthCheck associated with the Checker fails, Then will reject the HTTP request // with a 403. If only one of them passes, Then will call h. // This produces a very pleasant syntax for authenticating routes: // func listAccounts(ctx context.Context, w http.ResponseWriter, r *http.Request) { // w.Write([]byte("[]")) // } // // kami.Get("/accounts", auth.Check(auth.Super, auth.HasRole("admin")).Then(listAccounts)) func (c *Checker) Then(h kami.HandlerFunc) kami.HandlerFunc { return func(ctx context.Context, w http.ResponseWriter, r *http.Request) { var acct account.Account GetAccount(ctx, &acct) // run the checks var err error for _, check := range c.checks { err = check(ctx, w, r) if err != nil && c.all { break } else if err == nil && !c.all { break } } // did one of them pass? if err == nil { log.Infof(ctx, "%s: access granted", acct.Email) // mark this as a context which passed authentication ctx = context.WithValue(ctx, internal.AuthCheckContextKey, "passed") // run the underlying handler h(ctx, w, r) } else { log.Warningf(ctx, "%s: access denied", acct.Email) rest.WriteJSON(w, err) } } }