feat: deliver authenticated task APIs, SDK and P1 regression
This commit is contained in:
94
platform/auth/token.go
Normal file
94
platform/auth/token.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// 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{"tasks:submit": true, "tasks:read": true, "tasks:cancel": true, "queues:read": true, "queues: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
|
||||
}
|
||||
Reference in New Issue
Block a user