// 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 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 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) } }
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 } }
// Middleware ensures all requests are formatted properly for a REST/JSON API. // It requires that inbound requests meet all of the following conditions: // // The request accepts application/json. // The request's content type is application/json, if it has a body. // The request is encoded in UTF-8. // The request accepts UTF-8. // The request is properly configured for CORS, if it requires it. // // If any of these conditions is not met, Rest will respond with an appropriate // HTTP error code and error message. Otherwise it will pass control down the line. func Middleware(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context { w.Header().Set("Accept", "application/json") w.Header().Set("Accept-Charset", "UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PATCH, DELETE") var conf config.Global if err := config.Get(ctx, &conf); err != nil { panic("Could not retrieve Configuration for Rest middleware: " + err.Error()) } if !AcceptsJson(r) || !AcceptsUtf8(r) { // If the requester does not accept JSON in the UTF-8 character set, respond with // 406 Not Acceptable w.WriteHeader(http.StatusNotAcceptable) w.Write([]byte(`{"message": "This API only responds with application/json in UTF-8"}`)) return nil } else if r.Method != "HEAD" && r.Method != "GET" && r.Method != "OPTIONS" && !ContentIsJson(r) { // If the requester has sent something other than application/json, respond with // 415 Unsupported Media Type w.WriteHeader(http.StatusUnsupportedMediaType) w.Write([]byte(`{"message": "This API only accepts application/json in UTF-8"}`)) return nil } else if r.Header.Get("Origin") != "" && !HasValidOrigin(r, conf.ValidOriginSuffix) { // If the requester has sent Origin and the origin is invalid, respond with // 400 Bad Request w.Header().Set("Access-Control-Allow-Origin", conf.ValidOriginSuffix) w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, `{"message": "Invalid Origin header; this API only accepts %s and its subdomains"}`, conf.ValidOriginSuffix) return nil } else { // The request passes all checks; it can now be processed // Since "Access-Control-Allow-Origin" passed, set the request Origin // as an allowed origin if r.Header.Get("Origin") != "" { w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) w.Header().Set("Access-Control-Allow-Credentials", "true") } if r.Method == "OPTIONS" { // Options call. Intercept and do not forward. w.Header().Set("Access-Control-Allow-Headers", "Accept, Accept-Charset, Content-Type, Authorization, X-CORS") w.Header().Set("Access-Control-Expose-Headers", "Content-Type, Accept, Accept-Charset, Link, Location, X-CORS") w.Header().Set("Access-Control-Max-Age", "3600") w.WriteHeader(http.StatusNoContent) return nil } return ctx } }