mosquitto-go-auth/backends/jwt_javascript.go

180 lines
4.0 KiB
Go

package backends
import (
"strconv"
"github.com/iegomez/mosquitto-go-auth/backends/js"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
type jsJWTChecker struct {
stackDepthLimit int
msMaxDuration int64
userScript string
superuserScript string
aclScript string
passClaims bool
options tokenOptions
runner *js.Runner
}
func NewJsJWTChecker(authOpts map[string]string, options tokenOptions) (jwtChecker, error) {
checker := &jsJWTChecker{
stackDepthLimit: js.DefaultStackDepthLimit,
msMaxDuration: js.DefaultMsMaxDuration,
options: options,
}
if stackLimit, ok := authOpts["jwt_js_stack_depth_limit"]; ok {
limit, err := strconv.ParseInt(stackLimit, 10, 64)
if err != nil {
log.Errorf("invalid stack depth limit %s, defaulting to %d", stackLimit, js.DefaultStackDepthLimit)
} else {
checker.stackDepthLimit = int(limit)
}
}
if maxDuration, ok := authOpts["jwt_js_ms_max_duration"]; ok {
duration, err := strconv.ParseInt(maxDuration, 10, 64)
if err != nil {
log.Errorf("invalid stack depth limit %s, defaulting to %d", maxDuration, js.DefaultMsMaxDuration)
} else {
checker.msMaxDuration = duration
}
}
if userScriptPath, ok := authOpts["jwt_js_user_script_path"]; ok {
script, err := js.LoadScript(userScriptPath)
if err != nil {
return nil, err
}
checker.userScript = script
} else {
return nil, errors.New("missing jwt_js_user_script_path")
}
if superuserScriptPath, ok := authOpts["jwt_js_superuser_script_path"]; ok {
script, err := js.LoadScript(superuserScriptPath)
if err != nil {
return nil, err
}
checker.superuserScript = script
} else {
return nil, errors.New("missing jwt_js_superuser_script_path")
}
if aclScriptPath, ok := authOpts["jwt_js_acl_script_path"]; ok {
script, err := js.LoadScript(aclScriptPath)
if err != nil {
return nil, err
}
checker.aclScript = script
} else {
return nil, errors.New("missing jwt_js_acl_script_path")
}
if passClaims, ok := authOpts["jwt_js_pass_claims"]; ok && passClaims == "true" {
checker.passClaims = true
}
checker.runner = js.NewRunner(checker.stackDepthLimit, checker.msMaxDuration)
return checker, nil
}
func (o *jsJWTChecker) GetUser(token string) (bool, error) {
params := map[string]interface{}{
"token": token,
}
if o.options.parseToken {
var err error
if params, err = o.addDataFromJWT(params, token, o.options.skipUserExpiration); err != nil {
return false, err
}
}
granted, err := o.runner.RunScript(o.userScript, params)
if err != nil {
log.Errorf("js error: %s", err)
}
return granted, err
}
func (o *jsJWTChecker) addDataFromJWT(params map[string]interface{}, token string, skipExpiration bool) (map[string]interface{}, error) {
claims, err := getClaimsForToken(o.options, token, skipExpiration)
if err != nil {
log.Printf("jwt get claims error: %s", err)
return nil, err
}
if o.passClaims {
params["claims"] = claims
}
if username, found := claims[o.options.userFieldKey]; found {
params["username"] = username.(string)
} else {
params["username"] = ""
}
return params, nil
}
func (o *jsJWTChecker) GetSuperuser(token string) (bool, error) {
params := map[string]interface{}{
"token": token,
}
if o.options.parseToken {
var err error
if params, err = o.addDataFromJWT(params, token, o.options.skipUserExpiration); err != nil {
return false, err
}
}
granted, err := o.runner.RunScript(o.superuserScript, params)
if err != nil {
log.Errorf("js error: %s", err)
}
return granted, err
}
func (o *jsJWTChecker) CheckAcl(token, topic, clientid string, acc int32) (bool, error) {
params := map[string]interface{}{
"token": token,
"topic": topic,
"clientid": clientid,
"acc": acc,
}
if o.options.parseToken {
var err error
if params, err = o.addDataFromJWT(params, token, o.options.skipACLExpiration); err != nil {
return false, err
}
}
granted, err := o.runner.RunScript(o.aclScript, params)
if err != nil {
log.Errorf("js error: %s", err)
}
return granted, err
}
func (o *jsJWTChecker) Halt() {
// NO-OP
}