Files
orbit/platform/auth/token.go

95 lines
3.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package auth 定义凭据格式与权限快照;凭据原文仅在签发时交付。
package auth
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"strings"
"git.yuntuo.hk/k2safe/orbit/internal/fault"
core "git.yuntuo.hk/k2safe/orbit/model"
)
// Token 是已解析但尚未认证的身份,服务端仍须核对数据库摘要与有效期。
type Token struct {
// NamespaceID 凭据所属命名空间,不能由请求体覆盖。
NamespaceID core.ID
// CredentialID 凭据记录标识。
CredentialID core.ID
// Digest 来自原始秘密的摘要,不包含可重放的原始秘密。
Digest core.Digest
}
func Issue(namespace, credential core.ID) (string, core.Digest, error) {
if err := namespace.Validate(); err != nil {
return "", core.Digest{}, err
}
if err := credential.Validate(); err != nil {
return "", core.Digest{}, err
}
var secret [32]byte
if _, err := rand.Read(secret[:]); err != nil {
return "", core.Digest{}, err
}
return "orb1." + string(namespace) + "." + string(credential) + "." + base64.RawURLEncoding.EncodeToString(secret[:]), core.Digest(sha256.Sum256(secret[:])), nil
}
func Parse(value string) (Token, error) {
p := strings.Split(value, ".")
if len(p) != 4 || p[0] != "orb1" || len(p[3]) != 43 {
return Token{}, fault.Unauthorized
}
ns, id := core.ID(p[1]), core.ID(p[2])
if ns.Validate() != nil || id.Validate() != nil {
return Token{}, fault.Unauthorized
}
secret, err := base64.RawURLEncoding.Strict().DecodeString(p[3])
if err != nil || len(secret) != 32 {
return Token{}, fault.Unauthorized
}
return Token{NamespaceID: ns, CredentialID: id, Digest: core.Digest(sha256.Sum256(secret))}, nil
}
// Permissions 是凭据的授权快照;队列动作同时核验队列范围,显式应用动作作用于整个所属应用。
type Permissions struct {
// Actions 明确授权的动作集合,首版不使用动作通配符。
Actions []string `json:"actions"`
// Queues 可访问的队列 ID;星号表示当前命名空间内全部队列。
Queues []string `json:"queues"`
}
var supported = map[string]bool{"workflows:audit:read": true, "workflows:recover": true, "workflows:signal": true, "workflows:control": true, "workflows:start": true, "workflows:read": true, "workflows:definitions:read": true, "workflows:definitions:publish": true, "tasks:review": true, "tasks:retry": true, "tasks:audit:read": true, "events:read": true, "events:manage": true, "events:consume": true, "application:resources:read": true, "application:resources:manage": true, "tasks:submit": true, "tasks:read": true, "tasks:cancel": true, "queues:read": true, "queues:manage": true, "workers:connect": true, "workers:consume": true, "schedules:read": true, "schedules:manage": true}
func (p Permissions) Validate() error {
if len(p.Actions) == 0 || len(p.Actions) > 32 || len(p.Queues) == 0 || len(p.Queues) > 128 {
return fault.Invalid("invalid permission scope")
}
for _, a := range p.Actions {
if !supported[a] {
return fault.Invalid("unsupported permission action")
}
}
for _, q := range p.Queues {
if q != "*" && core.ID(q).Validate() != nil {
return fault.Invalid("invalid queue permission")
}
}
return nil
}
func (p Permissions) Allows(action string) bool {
for _, a := range p.Actions {
if a == action {
return true
}
}
return false
}
func (p Permissions) AllowsQueue(queue core.ID) bool {
for _, q := range p.Queues {
if q == "*" || q == string(queue) {
return true
}
}
return false
}