From 4f4fa2b3d7a2d997a9c5f9c9100532e2517cb95e Mon Sep 17 00:00:00 2001
From: Tom
+ A new token has been created.
+ This token will only be shown once.
+ Please make sure to copy it now.
+
+ {{ .Token.Token }}
+
+ To test this token, you can use curl on the command line:
+
+ You should receive a response with
+ curl -H "Authorization: Bearer {{ .Token.Token }}" {{ .Domain }}api/v1/auth
+
+ token: true inside of it.
+
This table shows tokens currently associated with your account. @@ -16,6 +16,9 @@
| + ID + | Token | @@ -32,7 +35,10 @@ {{ range .Tokens }}||
|---|---|---|---|
- {{ .Token }}
+ {{ .TokenID }}
+ |
+ + (only shown once) | {{ .Description }} @@ -40,7 +46,7 @@ |
+
+
\ No newline at end of file
diff --git a/internal/dis/component/auth/panel/tokens.go b/internal/dis/component/auth/panel/tokens.go
index e32f0c5..5481577 100644
--- a/internal/dis/component/auth/panel/tokens.go
+++ b/internal/dis/component/auth/panel/tokens.go
@@ -2,6 +2,7 @@ package panel
import (
"context"
+ "html/template"
"net/http"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
@@ -27,6 +28,7 @@ var tokensTemplate = templating.Parse[TokenTemplateContext](
type TokenTemplateContext struct {
templating.RuntimeFlags
+ Domain template.URL // server base URL
Tokens []models.Token
}
@@ -49,6 +51,8 @@ func (panel *UserPanel) tokensRoute(ctx context.Context) http.Handler {
return tc, err
}
+ tc.Domain = template.URL(panel.Config.HTTP.JoinPath().String())
+
// get the tokens
tc.Tokens, err = panel.Dependencies.Tokens.Tokens(r.Context(), user.User.User)
return tc, err
@@ -70,14 +74,14 @@ func (panel *UserPanel) tokensDeleteRoute(ctx context.Context) http.Handler {
return
}
- token := r.PostFormValue("token")
- if token == "" {
+ id := r.PostFormValue("id")
+ if id == "" {
logger.Err(err).Str("action", "delete token").Msg("failed to get token")
httpx.HTMLInterceptor.Fallback.ServeHTTP(w, r)
return
}
- if err := panel.Dependencies.Tokens.Remove(r.Context(), user.User.User, token); err != nil {
+ if err := panel.Dependencies.Tokens.Remove(r.Context(), user.User.User, id); err != nil {
logger.Err(err).Str("action", "delete token").Msg("failed to delete token")
httpx.HTMLInterceptor.Fallback.ServeHTTP(w, r)
return
@@ -101,8 +105,32 @@ type addTokenResult struct {
Scopes []string
}
+//go:embed "templates/token_created.html"
+var tokenCreatedHTML []byte
+var tokenCreateTemplate = templating.Parse[TokenCreateContext](
+ "token_created.html", tokenCreatedHTML, httpx.FormTemplate,
+ templating.Title("Add Token"),
+ templating.Assets(assets.AssetsUser),
+)
+
+type TokenCreateContext struct {
+ templating.RuntimeFlags
+
+ Domain template.URL // server base URL
+ Token *models.Token
+}
+
func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
- tpl := tokensAddTemplate.Prepare(
+ tplForm := tokensAddTemplate.Prepare(
+ panel.Dependencies.Templating,
+ templating.Crumbs(
+ menuUser,
+ menuTokens,
+ menuTokensAdd,
+ ),
+ )
+
+ tplDone := tokenCreateTemplate.Prepare(
panel.Dependencies.Templating,
templating.Crumbs(
menuUser,
@@ -117,8 +145,8 @@ func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
},
FieldTemplate: field.PureCSSFieldTemplate,
- RenderTemplate: tpl.Template(),
- RenderTemplateContext: templating.FormTemplateContext(tpl),
+ RenderTemplate: tplForm.Template(),
+ RenderTemplateContext: templating.FormTemplateContext(tplForm),
Validate: func(r *http.Request, values map[string]string) (at addTokenResult, err error) {
at.User, err = panel.Dependencies.Auth.UserOfSession(r)
@@ -138,16 +166,19 @@ func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
RenderSuccess: func(at addTokenResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
// add the key to the user
- _, err := panel.Dependencies.Tokens.Add(r.Context(), at.User.User.User, at.Description, at.Scopes)
+ tok, err := panel.Dependencies.Tokens.Add(r.Context(), at.User.User.User, at.Description, at.Scopes)
if err != nil {
return err
}
if err != nil {
return errAddToken
}
- // everything went fine, redirect the user back to the user page!
- http.Redirect(w, r, string(menuTokens.Path), http.StatusSeeOther)
- return nil
+
+ // render the created context
+ return httpx.WriteHTML(tplDone.Context(r, TokenCreateContext{
+ Domain: template.URL(panel.Config.HTTP.JoinPath().String()),
+ Token: tok,
+ }), nil, tplDone.Template(), "", w, r)
},
}
}
diff --git a/internal/dis/component/auth/panel/user.go b/internal/dis/component/auth/panel/user.go
index 017db1f..cceb7c2 100644
--- a/internal/dis/component/auth/panel/user.go
+++ b/internal/dis/component/auth/panel/user.go
@@ -41,18 +41,22 @@ func (g GrantWithURL) AdminURL() template.URL {
}
func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
+ actions := []component.MenuItem{
+ menuChangePassword,
+ menuTOTPAction,
+ menuSSH,
+ }
+
+ if panel.Config.HTTP.API.Value {
+ actions = append(actions, menuTokens)
+ }
tpl := userTemplate.Prepare(
panel.Dependencies.Templating,
templating.Crumbs(
menuUser,
),
- templating.Actions(
- menuChangePassword,
- menuTOTPAction,
- menuSSH,
- menuTokens,
- ),
+ templating.Actions(actions...),
)
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (uc userContext, funcs []templating.FlagFunc, err error) {
diff --git a/internal/dis/component/auth/tokens/tokens.go b/internal/dis/component/auth/tokens/tokens.go
index b77f8e2..52ca676 100644
--- a/internal/dis/component/auth/tokens/tokens.go
+++ b/internal/dis/component/auth/tokens/tokens.go
@@ -113,7 +113,16 @@ func (tok *Tokens) Add(ctx context.Context, user string, description string, sco
}
mk.SetScopes(scopes)
- // generate a new random password
+ // generate a new id for the token
+ {
+ var err error
+ mk.TokenID, err = NewToken()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // generate the actual token
var err error
mk.Token, err = NewToken()
if err != nil {
@@ -136,7 +145,7 @@ func (tok *Tokens) Add(ctx context.Context, user string, description string, sco
}
// Remove removes a token with the given token from the user
-func (tok *Tokens) Remove(ctx context.Context, user, token string) error {
+func (tok *Tokens) Remove(ctx context.Context, user, id string) error {
// get the table
table, err := tok.table(ctx)
if err != nil {
@@ -144,5 +153,5 @@ func (tok *Tokens) Remove(ctx context.Context, user, token string) error {
}
// and do the delete
- return table.Where("user = ? AND token = ?", user, token).Delete(&models.Token{}).Error
+ return table.Where("user = ? AND id = ?", user, id).Delete(&models.Token{}).Error
}
diff --git a/internal/models/token.go b/internal/models/token.go
index 9c50b22..5123437 100644
--- a/internal/models/token.go
+++ b/internal/models/token.go
@@ -11,8 +11,10 @@ const TokensTable = "tokens"
type Token struct {
Pk uint `gorm:"column:pk;primaryKey"`
- Token string `gorm:"column:token;unique:true;not null"`
- User string `gorm:"column:user;not null"` // (distillery) username
+ Token string `json:"-" gorm:"column:token;unique:true;not null"` // token used by the actual api (shown only once)
+ TokenID string `gorm:"column:id;unique:true;not null"` // token id (displayed to user, used for finding it)
+
+ User string `gorm:"column:user;not null"` // (distillery) username
Description string `gorm:"column:description"`
+ To check if a token is working, you can use something like: + +
+ curl -H "Authorization: Bearer <token>" {{ .Domain }}api/v1/auth
+
+
+ When using a working token, you should get a response with |