Skip to content

Commit

Permalink
👻 add hack to generate jwt token. (#534)
Browse files Browse the repository at this point in the history
Add hack script to generate jwt tokens for _builtin_ auth. Likley used
by actors such as the operator for migration related to upgrade.

Related:
- auth.Validator signature changed to return error. This is more
flexible and _failures_ can be propagated.
- auth.Builtin.Authenticate() returns NotAuthenticated when should have
returned NotValid for clearity.
- auth.NotValid includes `Reason`.

---------

Signed-off-by: Jeff Ortel <[email protected]>
  • Loading branch information
jortel authored Oct 24, 2023
1 parent 7fef625 commit 47623f1
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 31 deletions.
49 changes: 39 additions & 10 deletions auth/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ var Validators []Validator
// Validator provides token validation.
type Validator interface {
// Valid determines if the token is valid.
Valid(token *jwt.Token, db *gorm.DB) (valid bool)
// When valid, return nil.
// When not valid, return NotValid error.
// On failure, return the (cause) error.
Valid(token *jwt.Token, db *gorm.DB) (err error)
}

//
Expand Down Expand Up @@ -71,9 +74,15 @@ type Builtin struct {
//
// Authenticate the token
func (r *Builtin) Authenticate(request *Request) (jwToken *jwt.Token, err error) {
token := request.Token
token := strings.Replace(request.Token, "Bearer", "", 1)
token = strings.Fields(token)[0]
defer func() {
if err != nil {
Log.Info(err.Error())
}
}()
jwToken, err = jwt.Parse(
request.Token,
token,
func(jwToken *jwt.Token) (secret interface{}, err error) {
_, cast := jwToken.Method.(*jwt.SigningMethodHMAC)
if !cast {
Expand All @@ -93,32 +102,52 @@ func (r *Builtin) Authenticate(request *Request) (jwToken *jwt.Token, err error)
}
claims, cast := jwToken.Claims.(jwt.MapClaims)
if !cast {
err = liberr.Wrap(&NotAuthenticated{Token: token})
err = liberr.Wrap(
&NotValid{
Reason: "Claims not specified.",
Token: token,
})
return
}
v, found := claims["user"]
if !found {
err = liberr.Wrap(&NotAuthenticated{Token: token})
err = liberr.Wrap(
&NotValid{
Reason: "User not specified.",
Token: token,
})
return
}
_, cast = v.(string)
if !cast {
err = liberr.Wrap(&NotAuthenticated{Token: token})
err = liberr.Wrap(
&NotValid{
Reason: "User not string.",
Token: token,
})
return
}
v, found = claims["scope"]
if !found {
err = liberr.Wrap(&NotAuthenticated{Token: token})
err = liberr.Wrap(
&NotValid{
Reason: "Scope not specified.",
Token: token,
})
return
}
_, cast = v.(string)
if !cast {
err = liberr.Wrap(&NotAuthenticated{Token: token})
err = liberr.Wrap(
&NotValid{
Reason: "Scope not string.",
Token: token,
})
return
}
for _, v := range Validators {
if !v.Valid(jwToken, request.DB) {
err = liberr.Wrap(&NotValid{Token: token})
err = v.Valid(jwToken, request.DB)
if err != nil {
return
}
}
Expand Down
14 changes: 9 additions & 5 deletions auth/provider.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package auth

import (
"errors"
"fmt"
"github.com/golang-jwt/jwt/v4"
"github.com/jortel/go-utils/logr"
Expand Down Expand Up @@ -51,26 +52,29 @@ type NotAuthenticated struct {
}

func (e *NotAuthenticated) Error() (s string) {
return fmt.Sprintf("Token %s not-valid.", e.Token)
return fmt.Sprintf("Token [%s] not-authenticated.", e.Token)
}

func (e *NotAuthenticated) Is(err error) (matched bool) {
_, matched = err.(*NotAuthenticated)
notAuth := &NotAuthenticated{}
matched = errors.As(err, &notAuth)
return
}

//
// NotValid is returned when a token is not valid.
type NotValid struct {
Token string
Reason string
Token string
}

func (e *NotValid) Error() (s string) {
return fmt.Sprintf("Token %s not-valid.", e.Token)
return fmt.Sprintf("Token [%s] not-valid: %s", e.Token, e.Reason)
}

func (e *NotValid) Is(err error) (matched bool) {
_, matched = err.(*NotValid)
notValid := &NotValid{}
matched = errors.As(err, &notValid)
return
}

Expand Down
28 changes: 28 additions & 0 deletions hack/jwt.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash
#
# Usage: jwt.sh <key> <scope>
#
# scope - (string) space-separated scopes. (default: *:*).
#
key=$1
scope="${2:-*:*}"
header='{"typ":"JWT","alg":"HS512"}'
payload="{\"user\":\"operator\",\"scope\":\"${scope}\"}"
headerStr=$(echo -n ${header} \
| base64 -w 0 \
| sed s/\+/-/g \
| sed 's/\//_/g' \
| sed -E s/=+$//)
payloadStr=$(echo -n ${payload} \
| base64 -w 0 \
| sed s/\+/-/g \
| sed 's/\//_/g' \
| sed -E s/=+$//)
signStr=$(echo -n "${headerStr}.${payloadStr}" \
| openssl dgst -sha512 -hmac ${key} -binary \
| base64 -w 0 \
| sed s/\+/-/g \
| sed 's/\//_/g' \
| sed -E s/=+$//)
token="${headerStr}.${payloadStr}.${signStr}"
echo "${token}"
44 changes: 28 additions & 16 deletions task/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package task

import (
"context"
"fmt"
"github.com/golang-jwt/jwt/v4"
"github.com/konveyor/tackle2-hub/auth"
"github.com/konveyor/tackle2-hub/model"
"gorm.io/gorm"
core "k8s.io/api/core/v1"
Expand All @@ -22,26 +24,34 @@ type Validator struct {
// - The token references a task.
// - The task is valid and running.
// - The task pod valid and pending|running.
func (r *Validator) Valid(token *jwt.Token, db *gorm.DB) (valid bool) {
var err error
func (r *Validator) Valid(token *jwt.Token, db *gorm.DB) (err error) {
claims := token.Claims.(jwt.MapClaims)
v, found := claims["task"]
id, cast := v.(float64)
if !found || !cast {
Log.Info("Task not referenced by token.")
return
}
task := &model.Task{}
err = db.First(task, id).Error
if err != nil {
Log.Info("Task referenced by token: not found.")
err = &auth.NotValid{
Token: token.Raw,
Reason: fmt.Sprintf(
"Task (%d) referenced by token: not found.",
uint64(id)),
}
return
}
switch task.State {
case Pending,
Running:
default:
Log.Info("Task referenced by token: not running.")
err = &auth.NotValid{
Token: token.Raw,
Reason: fmt.Sprintf(
"Task (%d) referenced by token: not running.",
uint64(id)),
}
return
}
pod := &core.Pod{}
Expand All @@ -53,24 +63,26 @@ func (r *Validator) Valid(token *jwt.Token, db *gorm.DB) (valid bool) {
},
pod)
if err != nil {
Log.Info(
"Pod referenced by token: not found.",
"name",
task.Pod)
err = &auth.NotValid{
Token: token.Raw,
Reason: fmt.Sprintf(
"Pod (%s) referenced by token: not found.",
pod.Name),
}
return
}
switch pod.Status.Phase {
case core.PodPending,
core.PodRunning:
default:
Log.Info(
"Pod referenced by token: not running.",
"name",
task.Pod,
"phase",
pod.Status.Phase)
err = &auth.NotValid{
Token: token.Raw,
Reason: fmt.Sprintf(
"Pod (%s) referenced by token: not pending|running. Phase detected: %s",
task.Pod,
pod.Status.Phase),
}
return
}
valid = true
return
}

0 comments on commit 47623f1

Please sign in to comment.