feat: system api requires authenticated requests (#3570)

* begin auth

* feat: system api requires authenticated requests

* fix tests
This commit is contained in:
Livio Amstutz
2022-05-30 13:38:30 +02:00
committed by GitHub
parent 41d78ef523
commit 2fc39c0da0
15 changed files with 179 additions and 50 deletions

View File

@@ -2,6 +2,7 @@ package authz
import (
"context"
"strings"
"github.com/zitadel/zitadel/internal/api/grpc"
http_util "github.com/zitadel/zitadel/internal/api/http"
@@ -67,6 +68,9 @@ func VerifyTokenAndCreateCtxData(ctx context.Context, token, orgID string, t *To
if err != nil {
return CtxData{}, err
}
if strings.HasPrefix(method, "/zitadel.system.v1.SystemService") {
return CtxData{UserID: userID}, nil
}
var projectID string
var origins []string
if clientID != "" {

View File

@@ -68,7 +68,7 @@ func Test_GetUserMethodPermissions(t *testing.T) {
{
Roles: []string{"ORG_OWNER"},
},
}}),
}}, "", nil),
requiredPerm: "project.read",
authConfig: Config{
RolePermissionMappings: []RoleMapping{
@@ -91,7 +91,7 @@ func Test_GetUserMethodPermissions(t *testing.T) {
name: "No Grants",
args: args{
ctxData: CtxData{},
verifier: Start(&testVerifier{memberships: []*Membership{}}),
verifier: Start(&testVerifier{memberships: []*Membership{}}, "", nil),
requiredPerm: "project.read",
authConfig: Config{
RolePermissionMappings: []RoleMapping{
@@ -119,7 +119,7 @@ func Test_GetUserMethodPermissions(t *testing.T) {
MemberType: MemberTypeIam,
Roles: []string{"IAM_OWNER"},
},
}}),
}}, "", nil),
requiredPerm: "project.read",
authConfig: Config{
RolePermissionMappings: []RoleMapping{

View File

@@ -2,9 +2,16 @@ package authz
import (
"context"
"crypto/rsa"
"os"
"strings"
"sync"
"time"
"github.com/zitadel/oidc/v2/pkg/op"
"gopkg.in/square/go-jose.v2"
"github.com/zitadel/zitadel/internal/crypto"
caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
@@ -14,9 +21,10 @@ const (
)
type TokenVerifier struct {
authZRepo authZRepo
clients sync.Map
authMethods MethodMapping
authZRepo authZRepo
clients sync.Map
authMethods MethodMapping
systemJWTProfile op.JWTProfileVerifier
}
type authZRepo interface {
@@ -27,15 +35,74 @@ type authZRepo interface {
ExistsOrg(ctx context.Context, orgID string) error
}
func Start(authZRepo authZRepo) (v *TokenVerifier) {
return &TokenVerifier{authZRepo: authZRepo}
func Start(authZRepo authZRepo, systemAPI string, keys map[string]*SystemAPIUser) (v *TokenVerifier) {
return &TokenVerifier{
authZRepo: authZRepo,
systemJWTProfile: op.NewJWTProfileVerifier(
&systemJWTStorage{
keys: keys,
cachedKeys: make(map[string]*rsa.PublicKey),
},
systemAPI,
1*time.Hour,
time.Second,
),
}
}
func (v *TokenVerifier) VerifyAccessToken(ctx context.Context, token string, method string) (userID, clientID, agentID, prefLang, resourceOwner string, err error) {
if strings.HasPrefix(method, "/zitadel.system.v1.SystemService") {
userID, err := v.verifySystemToken(ctx, token)
if err != nil {
return "", "", "", "", "", err
}
return userID, "", "", "", "", nil
}
userID, agentID, clientID, prefLang, resourceOwner, err = v.authZRepo.VerifyAccessToken(ctx, token, "", GetInstance(ctx).ProjectID())
return userID, clientID, agentID, prefLang, resourceOwner, err
}
func (v *TokenVerifier) verifySystemToken(ctx context.Context, token string) (string, error) {
jwtReq, err := op.VerifyJWTAssertion(ctx, token, v.systemJWTProfile)
if err != nil {
return "", err
}
return jwtReq.Subject, nil
}
type systemJWTStorage struct {
keys map[string]*SystemAPIUser
mutex sync.Mutex
cachedKeys map[string]*rsa.PublicKey
}
type SystemAPIUser struct {
Path string
}
func (s *systemJWTStorage) GetKeyByIDAndUserID(_ context.Context, _, userID string) (*jose.JSONWebKey, error) {
cachedKey, ok := s.cachedKeys[userID]
if ok {
return &jose.JSONWebKey{KeyID: userID, Key: cachedKey}, nil
}
key, ok := s.keys[userID]
if !ok {
return nil, caos_errs.ThrowNotFound(nil, "AUTHZ-asfd3", "Errors.User.NotFound")
}
defer s.mutex.Unlock()
s.mutex.Lock()
keyData, err := os.ReadFile(key.Path)
if err != nil {
return nil, caos_errs.ThrowInternal(err, "AUTHZ-JK31F", "Errors.NotFound")
}
publicKey, err := crypto.BytesToPublicKey(keyData)
if err != nil {
return nil, err
}
s.cachedKeys[userID] = publicKey
return &jose.JSONWebKey{KeyID: userID, Key: publicKey}, nil
}
type client struct {
id string
projectID string