# HG changeset patch # User Peter Sanchez # Date 1682983665 21600 # Mon May 01 17:27:45 2023 -0600 # Node ID fe3bcabfd62f0ed4f841f2696e6bce2c5d4a1461 # Parent d771782b98279d06d80cb9d06b85145e6c2bf2bf Adding personal access tokens diff --git a/input.go b/input.go --- a/input.go +++ b/input.go @@ -1,12 +1,27 @@ package oauth2 import ( - "fmt" - "github.com/labstack/echo/v4" "hg.code.netlandish.com/~netlandish/gobwebs/validate" ) +type AddPersonalTokenForm struct { + Comment string `form:"comment" validate:"-"` +} + +func (a *AddPersonalTokenForm) Validate(c echo.Context) error { + // Binding each field specifically to use BindErrors + errs := validate.FormFieldBinder(c, a). + FailFast(false). + String("comment", &a.Comment). + BindErrors() + if errs != nil { + return validate.GetInputErrors(errs) + } + + return c.Validate(a) +} + type AddClientForm struct { Name string `form:"name" validate:"required"` Description string `form:"description" validate:"-"` @@ -27,9 +42,5 @@ return validate.GetInputErrors(errs) } - if err := c.Validate(a); err != nil { - return err - } - fmt.Println("foo") - return nil + return c.Validate(a) } diff --git a/models.go b/models.go --- a/models.go +++ b/models.go @@ -1,6 +1,9 @@ package oauth2 -import "time" +import ( + "database/sql" + "time" +) // Client ... type Client struct { @@ -20,11 +23,11 @@ // Grant ... type Grant struct { - ID int `db:"id"` - Issued time.Time `db:"issued"` - Expires time.Time `db:"expires"` - Comment time.Time `db:"comment"` - TokenHash string `db:"token_hash"` - UserID int `db:"user_id"` - ClientID int `db:"client_id"` + ID int `db:"id"` + Issued time.Time `db:"issued"` + Expires time.Time `db:"expires"` + Comment string `db:"comment"` + TokenHash string `db:"token_hash"` + UserID int `db:"user_id"` + ClientID sql.NullInt64 `db:"client_id"` } diff --git a/routes.go b/routes.go --- a/routes.go +++ b/routes.go @@ -1,8 +1,11 @@ package oauth2 import ( + "crypto/sha512" + "encoding/hex" "fmt" "net/http" + "time" sq "github.com/Masterminds/squirrel" "github.com/labstack/echo/v4" @@ -24,11 +27,81 @@ s.eg.POST("/introspect", s.Introspect).Name = s.RouteName("introspect_post") s.eg.Use(auth.AuthRequired()) + s.eg.GET("/personal", s.ListPersonal).Name = s.RouteName("list_personal") + s.eg.GET("/personal/add", s.AddPersonal).Name = s.RouteName("add_personal") + s.eg.POST("/personal/add", s.AddPersonal).Name = s.RouteName("add_personal_post") s.eg.GET("/clients", s.ListClients).Name = s.RouteName("list_clients") s.eg.GET("/clients/add", s.AddClient).Name = s.RouteName("add_client") s.eg.POST("/clients/add", s.AddClient).Name = s.RouteName("add_client_post") } +// ListPersonal ... +func (s *Service) ListPersonal(c echo.Context) error { + gctx := c.(*server.Context) + opts := &database.FilterOptions{ + Filter: sq.And{ + sq.Eq{"user_id": gctx.User.GetID()}, + sq.Expr("client_id IS NULL"), + sq.Expr("expires > NOW() at time zone 'UTC'"), + }, + } + tokens, err := GetGrants(c.Request().Context(), opts) + if err != nil { + return err + } + return gctx.Render(http.StatusOK, "oauth2_personal_list.html", gobwebs.Map{ + "tokens": tokens, + }) +} + +// AddPersonal ... +func (s *Service) AddPersonal(c echo.Context) error { + gctx := c.(*server.Context) + form := &AddPersonalTokenForm{} + gmap := gobwebs.Map{ + "form": form, + } + + req := c.Request() + if req.Method == "POST" { + if err := form.Validate(c); err != nil { + gmap["errors"] = err + return gctx.Render(http.StatusOK, "oauth2_add_personal.html", gmap) + } + + issued := time.Now().UTC() + expires := issued.Add(366 * 24 * time.Hour) + + grant := BearerToken{ + Version: TokenVersion, + Issued: ToTimestamp(issued), + Expires: ToTimestamp(expires), + Grants: "", + UserID: int(gctx.User.GetID()), + ClientID: "", + } + token := grant.Encode(c.Request().Context()) + hash := sha512.Sum512([]byte(token)) + tokenHash := hex.EncodeToString(hash[:]) + + dbgrant := &Grant{ + Issued: issued, + Expires: expires, + Comment: form.Comment, + TokenHash: tokenHash, + UserID: int(gctx.User.GetID()), + } + + if err := dbgrant.Store(c.Request().Context()); err != nil { + return err + } + gmap["token"] = token + return gctx.Render(http.StatusOK, "oauth2_add_personal_done.html", gmap) + } + + return gctx.Render(http.StatusOK, "oauth2_add_personal.html", gmap) +} + // ListClients ... func (s *Service) ListClients(c echo.Context) error { gctx := c.(*server.Context) @@ -44,7 +117,7 @@ }) } -// AddClient +// AddClient ... func (s *Service) AddClient(c echo.Context) error { gctx := c.(*server.Context) form := &AddClientForm{}