You've already forked golang-saas-starter-kit
mirror of
https://github.com/raseels-repos/golang-saas-starter-kit.git
synced 2025-06-29 00:52:03 +02:00
Added api example error responses and some minor bug fixes
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -10,9 +11,12 @@ import (
|
|||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/webcontext"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
_ "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
_ "geeks-accelerator/oss/saas-starter-kit/internal/platform/web/weberror"
|
||||||
|
"geeks-accelerator/oss/saas-starter-kit/internal/project"
|
||||||
_ "geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
_ "geeks-accelerator/oss/saas-starter-kit/internal/signup"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
"gopkg.in/DataDog/dd-trace-go.v1/contrib/go-redis/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,6 +96,8 @@ func API(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, masterDB
|
|||||||
app.Handle("PATCH", "/v1/projects/archive", p.Archive, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("PATCH", "/v1/projects/archive", p.Archive, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
app.Handle("DELETE", "/v1/projects/:id", p.Delete, mid.AuthenticateHeader(authenticator), mid.HasRole(auth.RoleAdmin))
|
||||||
|
|
||||||
|
app.Handle("GET", "/v1/examples/error-response", ExampleErrorResponse)
|
||||||
|
|
||||||
// Register swagger documentation.
|
// Register swagger documentation.
|
||||||
// TODO: Add authentication. Current authenticator requires an Authorization header
|
// TODO: Add authentication. Current authenticator requires an Authorization header
|
||||||
// which breaks the browser experience.
|
// which breaks the browser experience.
|
||||||
@ -101,6 +107,36 @@ func API(shutdown chan os.Signal, log *log.Logger, env webcontext.Env, masterDB
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExampleErrorResponse returns example error messages.
|
||||||
|
func ExampleErrorResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
|
v, err := webcontext.ContextValues(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if qv := r.URL.Query().Get("test-validation-error"); qv != "" {
|
||||||
|
_, err := project.Create(ctx, auth.Claims{}, nil, project.ProjectCreateRequest{}, v.Now)
|
||||||
|
return web.RespondJsonError(ctx, w, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if qv := r.URL.Query().Get("test-web-error"); qv != "" {
|
||||||
|
terr := errors.New("Some random error")
|
||||||
|
terr = errors.WithMessage(terr, "Actual error message")
|
||||||
|
rerr := weberror.NewError(ctx, terr, http.StatusBadRequest).(*weberror.Error)
|
||||||
|
rerr.Message = "Test Web Error Message"
|
||||||
|
return web.RespondJsonError(ctx, w, rerr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if qv := r.URL.Query().Get("test-error"); qv != "" {
|
||||||
|
terr := errors.New("Test error")
|
||||||
|
terr = errors.WithMessage(terr, "Error message")
|
||||||
|
return web.RespondJsonError(ctx, w, terr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Types godoc
|
// Types godoc
|
||||||
// @Summary List of types.
|
// @Summary List of types.
|
||||||
// @Param data body weberror.FieldError false "Field Error"
|
// @Param data body weberror.FieldError false "Field Error"
|
||||||
|
@ -36,11 +36,7 @@ func (h *Root) Index(ctx context.Context, w http.ResponseWriter, r *http.Request
|
|||||||
|
|
||||||
// indexDashboard loads the dashboard for a user when they are authenticated.
|
// indexDashboard loads the dashboard for a user when they are authenticated.
|
||||||
func (h *Root) indexDashboard(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
func (h *Root) indexDashboard(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
|
||||||
data := map[string]interface{}{
|
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "root-dashboard.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, nil)
|
||||||
"imgSizes": []int{100, 200, 300, 400, 500},
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.Renderer.Render(ctx, w, r, TmplLayoutBase, "root-dashboard.gohtml", web.MIMETextHTMLCharsetUTF8, http.StatusOK, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexDefault loads the root index page when a user has no authentication.
|
// indexDefault loads the root index page when a user has no authentication.
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/pborman/uuid"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -824,10 +823,6 @@ func handleSessionToken(ctx context.Context, db *sqlx.DB, w http.ResponseWriter,
|
|||||||
|
|
||||||
sess := webcontext.ContextSession(ctx)
|
sess := webcontext.ContextSession(ctx)
|
||||||
|
|
||||||
if sess.IsNew {
|
|
||||||
sess.ID = uuid.NewRandom().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
sess.Options = &sessions.Options{
|
sess.Options = &sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: int(token.TTL.Seconds()),
|
MaxAge: int(token.TTL.Seconds()),
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink" x-placement="bottom-end" style="position: absolute; transform: translate3d(-156px, 19px, 0px); top: 0px; left: 0px; will-change: transform;">
|
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink" x-placement="bottom-end" style="position: absolute; transform: translate3d(-156px, 19px, 0px); top: 0px; left: 0px; will-change: transform;">
|
||||||
<div class="dropdown-header">Actions</div>
|
<div class="dropdown-header">Actions</div>
|
||||||
<a class="dropdown-item" href="/account/update">Update Details</a>
|
<a class="dropdown-item" href="/user/update">Update Details</a>
|
||||||
<a class="dropdown-item" href="https://gravatar.com" target="_blank">Update Avatar</a>
|
<a class="dropdown-item" href="https://gravatar.com" target="_blank">Update Avatar</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,13 +125,14 @@
|
|||||||
{{ if or ($errMsg) ($errDetails) }}
|
{{ if or ($errMsg) ($errDetails) }}
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> {{ if $errMsg }}<h3>{{ $errMsg }}</h3> {{end}}
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <span aria-hidden="true">×</span> </button> {{ if $errMsg }}<h3>{{ $errMsg }}</h3> {{end}}
|
||||||
{{ if .error.Fields }}
|
{{ if HasField .error "Fields" }}
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $i := .error.Fields }}
|
{{ range $i := .error.Fields }}
|
||||||
<li>{{ if $i.Display }}{{ $i.Display }}{{ else }}{{ $i.Error }}{{ end }}</li>
|
<li>{{ if $i.Display }}{{ $i.Display }}{{ else }}{{ $i.Error }}{{ end }}</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ if $errDetails }}
|
{{ if $errDetails }}
|
||||||
<p><small>{{ $errDetails }}</small></p>
|
<p><small>{{ $errDetails }}</small></p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/cmd/web-app/handlers"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
var a http.Handler
|
|
||||||
var test *tests.Test
|
|
||||||
|
|
||||||
// Information about the users we have created for testing.
|
|
||||||
var adminAuthorization string
|
|
||||||
var adminID string
|
|
||||||
var userAuthorization string
|
|
||||||
var userID string
|
|
||||||
|
|
||||||
// TestMain is the entry point for testing.
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
os.Exit(testMain(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMain(m *testing.M) int {
|
|
||||||
test = tests.New()
|
|
||||||
defer test.TearDown()
|
|
||||||
|
|
||||||
// Create RSA keys to enable authentication in our service.
|
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
kid := "4754d86b-7a6d-4df5-9c65-224741361492"
|
|
||||||
kf := auth.NewSingleKeyFunc(kid, key.Public().(*rsa.PublicKey))
|
|
||||||
authenticator, err := auth.NewAuthenticator(key, kid, "RS256", kf)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown := make(chan os.Signal, 1)
|
|
||||||
a = handlers.API(shutdown, test.Log, test.MasterDB, authenticator)
|
|
||||||
|
|
||||||
// Create an admin user directly with our business logic. This creates an
|
|
||||||
// initial user that we will use for admin validated endpoints.
|
|
||||||
nu := user.NewUser{
|
|
||||||
Email: "admin@ardanlabs.com",
|
|
||||||
Name: "Admin User",
|
|
||||||
Roles: []string{auth.RoleAdmin, auth.RoleUser},
|
|
||||||
Password: "gophers",
|
|
||||||
PasswordConfirm: "gophers",
|
|
||||||
}
|
|
||||||
|
|
||||||
admin, err := user.Create(tests.Context(), test.MasterDB, &nu, time.Now())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
adminID = admin.ID.Hex()
|
|
||||||
|
|
||||||
tkn, err := user.Authenticate(tests.Context(), test.MasterDB, authenticator, time.Now(), nu.Email, nu.Password)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
adminAuthorization = "Bearer " + tkn.Token
|
|
||||||
|
|
||||||
// Create a regular user to use when calling regular validated endpoints.
|
|
||||||
nu = user.NewUser{
|
|
||||||
Email: "user@ardanlabs.com",
|
|
||||||
Name: "Regular User",
|
|
||||||
Roles: []string{auth.RoleUser},
|
|
||||||
Password: "concurrency",
|
|
||||||
PasswordConfirm: "concurrency",
|
|
||||||
}
|
|
||||||
|
|
||||||
usr, err := user.Create(tests.Context(), test.MasterDB, &nu, time.Now())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
userID = usr.ID.Hex()
|
|
||||||
|
|
||||||
tkn, err = user.Authenticate(tests.Context(), test.MasterDB, authenticator, time.Now(), nu.Email, nu.Password)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
userAuthorization = "Bearer " + tkn.Token
|
|
||||||
|
|
||||||
return m.Run()
|
|
||||||
}
|
|
@ -1,576 +0,0 @@
|
|||||||
package tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/platform/web"
|
|
||||||
"geeks-accelerator/oss/saas-starter-kit/internal/user"
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestUsers is the entry point for testing user management functions.
|
|
||||||
func TestUsers(t *testing.T) {
|
|
||||||
defer tests.Recover(t)
|
|
||||||
|
|
||||||
t.Run("getToken401", getToken401)
|
|
||||||
t.Run("getToken200", getToken200)
|
|
||||||
t.Run("postUser400", postUser400)
|
|
||||||
t.Run("postUser401", postUser401)
|
|
||||||
t.Run("postUser403", postUser403)
|
|
||||||
t.Run("getUser400", getUser400)
|
|
||||||
t.Run("getUser403", getUser403)
|
|
||||||
t.Run("getUser404", getUser404)
|
|
||||||
t.Run("deleteUser404", deleteUser404)
|
|
||||||
t.Run("putUser404", putUser404)
|
|
||||||
t.Run("crudUsers", crudUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getToken401 ensures an unknown user can't generate a token.
|
|
||||||
func getToken401(t *testing.T) {
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/token", nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.SetBasicAuth("unknown@example.com", "some-password")
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to deny tokens to unknown users.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen fetching a token with an unrecognized email.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusUnauthorized {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 401 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 401 for the response.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getToken200
|
|
||||||
func getToken200(t *testing.T) {
|
|
||||||
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/token", nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.SetBasicAuth("admin@ardanlabs.com", "gophers")
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to issues tokens to known users.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen fetching a token with valid credentials.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusOK {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 200 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 200 for the response.", tests.Success)
|
|
||||||
|
|
||||||
var got user.Token
|
|
||||||
if err := json.NewDecoder(w.Body).Decode(&got); err != nil {
|
|
||||||
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould be able to unmarshal the response.", tests.Success)
|
|
||||||
|
|
||||||
// TODO(jlw) Should we ensure the token is valid?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// postUser400 validates a user can't be created with the endpoint
|
|
||||||
// unless a valid user document is submitted.
|
|
||||||
func postUser400(t *testing.T) {
|
|
||||||
body, err := json.Marshal(&user.NewUser{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRequest("POST", "/v1/users", bytes.NewBuffer(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate a new user can't be created with an invalid document.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen using an incomplete user value.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusBadRequest {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 400 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 400 for the response.", tests.Success)
|
|
||||||
|
|
||||||
// Inspect the response.
|
|
||||||
var got web.ErrorResponse
|
|
||||||
if err := json.NewDecoder(w.Body).Decode(&got); err != nil {
|
|
||||||
t.Fatalf("\t%s\tShould be able to unmarshal the response to an error type : %v", tests.Failed, err)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould be able to unmarshal the response to an error type.", tests.Success)
|
|
||||||
|
|
||||||
// Define what we want to see.
|
|
||||||
want := web.ErrorResponse{
|
|
||||||
Error: "field validation error",
|
|
||||||
Fields: []web.FieldError{
|
|
||||||
{Field: "name", Error: "name is a required field"},
|
|
||||||
{Field: "email", Error: "email is a required field"},
|
|
||||||
{Field: "roles", Error: "roles is a required field"},
|
|
||||||
{Field: "password", Error: "password is a required field"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't rely on the order of the field errors so they have to be
|
|
||||||
// sorted. Tell the cmp package how to sort them.
|
|
||||||
sorter := cmpopts.SortSlices(func(a, b web.FieldError) bool {
|
|
||||||
return a.Field < b.Field
|
|
||||||
})
|
|
||||||
|
|
||||||
if diff := cmp.Diff(want, got, sorter); diff != "" {
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result. Diff:\n%s", tests.Failed, diff)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// postUser401 validates a user can't be created unless the calling user is
|
|
||||||
// authenticated.
|
|
||||||
func postUser401(t *testing.T) {
|
|
||||||
body, err := json.Marshal(&user.User{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRequest("POST", "/v1/users", bytes.NewBuffer(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", userAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate a new user can't be created with an invalid document.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen using an incomplete user value.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusForbidden {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 403 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 403 for the response.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// postUser403 validates a user can't be created unless the calling user is
|
|
||||||
// an admin user. Regular users can't do this.
|
|
||||||
func postUser403(t *testing.T) {
|
|
||||||
body, err := json.Marshal(&user.User{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRequest("POST", "/v1/users", bytes.NewBuffer(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
// Not setting the Authorization header
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate a new user can't be created with an invalid document.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen using an incomplete user value.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusUnauthorized {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 401 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 401 for the response.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUser400 validates a user request for a malformed userid.
|
|
||||||
func getUser400(t *testing.T) {
|
|
||||||
id := "12345"
|
|
||||||
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/"+id, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate getting a user with a malformed userid.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen using the new user %s.", id)
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusBadRequest {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 400 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 400 for the response.", tests.Success)
|
|
||||||
|
|
||||||
recv := w.Body.String()
|
|
||||||
resp := `{"error":"ID is not in its proper form"}`
|
|
||||||
if resp != recv {
|
|
||||||
t.Log("Got :", recv)
|
|
||||||
t.Log("Want:", resp)
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUser403 validates a regular user can't fetch anyone but themselves
|
|
||||||
func getUser403(t *testing.T) {
|
|
||||||
t.Log("Given the need to validate regular users can't fetch other users.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen fetching the admin user as a regular user.")
|
|
||||||
{
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/"+adminID, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", userAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
if w.Code != http.StatusForbidden {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 403 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 403 for the response.", tests.Success)
|
|
||||||
|
|
||||||
recv := w.Body.String()
|
|
||||||
resp := `{"error":"Attempted action is not allowed"}`
|
|
||||||
if resp != recv {
|
|
||||||
t.Log("Got :", recv)
|
|
||||||
t.Log("Want:", resp)
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("\tTest 1:\tWhen fetching the user as a themselves.")
|
|
||||||
{
|
|
||||||
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/"+userID, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", userAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
if w.Code != http.StatusOK {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 200 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 200 for the response.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUser404 validates a user request for a user that does not exist with the endpoint.
|
|
||||||
func getUser404(t *testing.T) {
|
|
||||||
id := bson.NewObjectId().Hex()
|
|
||||||
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/"+id, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate getting a user with an unknown id.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen using the new user %s.", id)
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusNotFound {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 404 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 404 for the response.", tests.Success)
|
|
||||||
|
|
||||||
recv := w.Body.String()
|
|
||||||
resp := "Entity not found"
|
|
||||||
if !strings.Contains(recv, resp) {
|
|
||||||
t.Log("Got :", recv)
|
|
||||||
t.Log("Want:", resp)
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deleteUser404 validates deleting a user that does not exist.
|
|
||||||
func deleteUser404(t *testing.T) {
|
|
||||||
id := bson.NewObjectId().Hex()
|
|
||||||
|
|
||||||
r := httptest.NewRequest("DELETE", "/v1/users/"+id, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate deleting a user that does not exist.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen using the new user %s.", id)
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusNotFound {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 404 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 404 for the response.", tests.Success)
|
|
||||||
|
|
||||||
recv := w.Body.String()
|
|
||||||
resp := "Entity not found"
|
|
||||||
if !strings.Contains(recv, resp) {
|
|
||||||
t.Log("Got :", recv)
|
|
||||||
t.Log("Want:", resp)
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// putUser404 validates updating a user that does not exist.
|
|
||||||
func putUser404(t *testing.T) {
|
|
||||||
u := user.UpdateUser{
|
|
||||||
Name: tests.StringPointer("Doesn't Exist"),
|
|
||||||
}
|
|
||||||
|
|
||||||
id := bson.NewObjectId().Hex()
|
|
||||||
|
|
||||||
body, err := json.Marshal(&u)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRequest("PUT", "/v1/users/"+id, bytes.NewBuffer(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate updating a user that does not exist.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen using the new user %s.", id)
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusNotFound {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 404 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 404 for the response.", tests.Success)
|
|
||||||
|
|
||||||
recv := w.Body.String()
|
|
||||||
resp := "Entity not found"
|
|
||||||
if !strings.Contains(recv, resp) {
|
|
||||||
t.Log("Got :", recv)
|
|
||||||
t.Log("Want:", resp)
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result.", tests.Failed)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// crudUser performs a complete test of CRUD against the api.
|
|
||||||
func crudUser(t *testing.T) {
|
|
||||||
nu := postUser201(t)
|
|
||||||
defer deleteUser204(t, nu.ID.Hex())
|
|
||||||
|
|
||||||
getUser200(t, nu.ID.Hex())
|
|
||||||
putUser204(t, nu.ID.Hex())
|
|
||||||
putUser403(t, nu.ID.Hex())
|
|
||||||
}
|
|
||||||
|
|
||||||
// postUser201 validates a user can be created with the endpoint.
|
|
||||||
func postUser201(t *testing.T) user.User {
|
|
||||||
nu := user.NewUser{
|
|
||||||
Name: "Bill Kennedy",
|
|
||||||
Email: "bill@ardanlabs.com",
|
|
||||||
Roles: []string{auth.RoleAdmin},
|
|
||||||
Password: "gophers",
|
|
||||||
PasswordConfirm: "gophers",
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(&nu)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRequest("POST", "/v1/users", bytes.NewBuffer(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
// u is the value we will return.
|
|
||||||
var u user.User
|
|
||||||
|
|
||||||
t.Log("Given the need to create a new user with the users endpoint.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen using the declared user value.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusCreated {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 201 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 201 for the response.", tests.Success)
|
|
||||||
|
|
||||||
if err := json.NewDecoder(w.Body).Decode(&u); err != nil {
|
|
||||||
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define what we wanted to receive. We will just trust the generated
|
|
||||||
// fields like ID and Dates so we copy u.
|
|
||||||
want := u
|
|
||||||
want.Name = "Bill Kennedy"
|
|
||||||
want.Email = "bill@ardanlabs.com"
|
|
||||||
want.Roles = []string{auth.RoleAdmin}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(want, u); diff != "" {
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result. Diff:\n%s", tests.Failed, diff)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// deleteUser200 validates deleting a user that does exist.
|
|
||||||
func deleteUser204(t *testing.T, id string) {
|
|
||||||
r := httptest.NewRequest("DELETE", "/v1/users/"+id, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate deleting a user that does exist.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen using the new user %s.", id)
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusNoContent {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 204 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 204 for the response.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUser200 validates a user request for an existing userid.
|
|
||||||
func getUser200(t *testing.T, id string) {
|
|
||||||
r := httptest.NewRequest("GET", "/v1/users/"+id, nil)
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to validate getting a user that exsits.")
|
|
||||||
{
|
|
||||||
t.Logf("\tTest 0:\tWhen using the new user %s.", id)
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusOK {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 200 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 200 for the response.", tests.Success)
|
|
||||||
|
|
||||||
var u user.User
|
|
||||||
if err := json.NewDecoder(w.Body).Decode(&u); err != nil {
|
|
||||||
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define what we wanted to receive. We will just trust the generated
|
|
||||||
// fields like Dates so we copy p.
|
|
||||||
want := u
|
|
||||||
want.ID = bson.ObjectIdHex(id)
|
|
||||||
want.Name = "Bill Kennedy"
|
|
||||||
want.Email = "bill@ardanlabs.com"
|
|
||||||
want.Roles = []string{auth.RoleAdmin}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(want, u); diff != "" {
|
|
||||||
t.Fatalf("\t%s\tShould get the expected result. Diff:\n%s", tests.Failed, diff)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould get the expected result.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// putUser204 validates updating a user that does exist.
|
|
||||||
func putUser204(t *testing.T, id string) {
|
|
||||||
body := `{"name": "Jacob Walker"}`
|
|
||||||
|
|
||||||
r := httptest.NewRequest("PUT", "/v1/users/"+id, strings.NewReader(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to update a user with the users endpoint.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen using the modified user value.")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusNoContent {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 204 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 204 for the response.", tests.Success)
|
|
||||||
|
|
||||||
r = httptest.NewRequest("GET", "/v1/users/"+id, nil)
|
|
||||||
w = httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", adminAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
if w.Code != http.StatusOK {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 200 for the retrieve : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 200 for the retrieve.", tests.Success)
|
|
||||||
|
|
||||||
var ru user.User
|
|
||||||
if err := json.NewDecoder(w.Body).Decode(&ru); err != nil {
|
|
||||||
t.Fatalf("\t%s\tShould be able to unmarshal the response : %v", tests.Failed, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ru.Name != "Jacob Walker" {
|
|
||||||
t.Fatalf("\t%s\tShould see an updated Name : got %q want %q", tests.Failed, ru.Name, "Jacob Walker")
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould see an updated Name.", tests.Success)
|
|
||||||
|
|
||||||
if ru.Email != "bill@ardanlabs.com" {
|
|
||||||
t.Fatalf("\t%s\tShould not affect other fields like Email : got %q want %q", tests.Failed, ru.Email, "bill@ardanlabs.com")
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould not affect other fields like Email.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// putUser403 validates that a user can't modify users unless they are an admin.
|
|
||||||
func putUser403(t *testing.T, id string) {
|
|
||||||
body := `{"name": "Anna Walker"}`
|
|
||||||
|
|
||||||
r := httptest.NewRequest("PUT", "/v1/users/"+id, strings.NewReader(body))
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
r.Header.Set("Authorization", userAuthorization)
|
|
||||||
|
|
||||||
a.ServeHTTP(w, r)
|
|
||||||
|
|
||||||
t.Log("Given the need to update a user with the users endpoint.")
|
|
||||||
{
|
|
||||||
t.Log("\tTest 0:\tWhen a non-admin user makes a request")
|
|
||||||
{
|
|
||||||
if w.Code != http.StatusForbidden {
|
|
||||||
t.Fatalf("\t%s\tShould receive a status code of 403 for the response : %v", tests.Failed, w.Code)
|
|
||||||
}
|
|
||||||
t.Logf("\t%s\tShould receive a status code of 403 for the response.", tests.Success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -135,7 +135,16 @@ func NewTemplate(templateFuncs template.FuncMap) *Template {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
"HasField": func(v interface{}, name string) bool {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.Kind() == reflect.Ptr {
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
if rv.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return rv.FieldByName(name).IsValid()
|
||||||
|
},
|
||||||
"dict": func(values ...interface{}) (map[string]interface{}, error) {
|
"dict": func(values ...interface{}) (map[string]interface{}, error) {
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
return nil, errors.New("invalid dict call")
|
return nil, errors.New("invalid dict call")
|
||||||
|
@ -2,7 +2,9 @@ package webcontext
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
"github.com/pborman/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ctxKeySession represents the type of value for the context key.
|
// ctxKeySession represents the type of value for the context key.
|
||||||
@ -16,6 +18,9 @@ const (
|
|||||||
SessionKeyAccessToken = iota
|
SessionKeyAccessToken = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// KeySessionID is the key used to store the ID of the session in its values.
|
||||||
|
const KeySessionID = "_sid"
|
||||||
|
|
||||||
// ContextWithSession appends a universal translator to a context.
|
// ContextWithSession appends a universal translator to a context.
|
||||||
func ContextWithSession(ctx context.Context, session *sessions.Session) context.Context {
|
func ContextWithSession(ctx context.Context, session *sessions.Session) context.Context {
|
||||||
return context.WithValue(ctx, KeySession, session)
|
return context.WithValue(ctx, KeySession, session)
|
||||||
@ -24,11 +29,16 @@ func ContextWithSession(ctx context.Context, session *sessions.Session) context.
|
|||||||
// ContextSession returns the session from a context.
|
// ContextSession returns the session from a context.
|
||||||
func ContextSession(ctx context.Context) *sessions.Session {
|
func ContextSession(ctx context.Context) *sessions.Session {
|
||||||
if s, ok := ctx.Value(KeySession).(*sessions.Session); ok {
|
if s, ok := ctx.Value(KeySession).(*sessions.Session); ok {
|
||||||
|
if sid, ok := s.Values[KeySessionID].(string); ok {
|
||||||
|
s.ID = sid
|
||||||
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContextAccessToken returns the JWT access token from the context session.
|
||||||
func ContextAccessToken(ctx context.Context) (string, bool) {
|
func ContextAccessToken(ctx context.Context) (string, bool) {
|
||||||
sess := ContextSession(ctx)
|
sess := ContextSession(ctx)
|
||||||
if sess == nil {
|
if sess == nil {
|
||||||
@ -40,18 +50,28 @@ func ContextAccessToken(ctx context.Context) (string, bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SessionInit creates a new session with a valid JWT access token.
|
||||||
func SessionInit(session *sessions.Session, accessToken string) *sessions.Session {
|
func SessionInit(session *sessions.Session, accessToken string) *sessions.Session {
|
||||||
|
|
||||||
|
// Always create a new session ID to ensure when session ID is being used as a cache key, logout/login
|
||||||
|
// forces any cache to be flushed.
|
||||||
|
session.ID = uuid.NewRandom().String()
|
||||||
|
|
||||||
|
// Not sure why sessions.Session has the ID prop but it is not persisted by default.
|
||||||
|
session.Values[KeySessionID] = session.ID
|
||||||
|
|
||||||
session.Values[SessionKeyAccessToken] = accessToken
|
session.Values[SessionKeyAccessToken] = accessToken
|
||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SessionUpdateAccessToken updates the JWT access token stored in the session.
|
||||||
func SessionUpdateAccessToken(session *sessions.Session, accessToken string) *sessions.Session {
|
func SessionUpdateAccessToken(session *sessions.Session, accessToken string) *sessions.Session {
|
||||||
session.Values[SessionKeyAccessToken] = accessToken
|
session.Values[SessionKeyAccessToken] = accessToken
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SessionDestroy removes the access token from the session which revokes authentication for the user.
|
||||||
func SessionDestroy(session *sessions.Session) *sessions.Session {
|
func SessionDestroy(session *sessions.Session) *sessions.Session {
|
||||||
|
|
||||||
delete(session.Values, SessionKeyAccessToken)
|
delete(session.Values, SessionKeyAccessToken)
|
||||||
|
Reference in New Issue
Block a user