Skip to content

Commit 0071a43

Browse files
authored
feature:接入系统区分单租户和全租户 (#338)
1 parent fe244f5 commit 0071a43

16 files changed

Lines changed: 342 additions & 152 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE `bkiam`.`saas_system_info` ADD COLUMN `tenant_id` VARCHAR(32) NOT NULL DEFAULT '';

pkg/api/model/handler/system.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
package handler
1212

1313
import (
14+
"errors"
15+
"fmt"
16+
1417
"github.com/TencentBlueKing/gopkg/collection/set"
1518
"github.com/TencentBlueKing/gopkg/errorx"
1619
"github.com/fatih/structs"
@@ -24,6 +27,29 @@ import (
2427
"iam/pkg/util"
2528
)
2629

30+
// getAppTenantID 根据当前请求的网关 JWT 信息确定创建/更新 system 时应使用的 tenant_id
31+
// - 全租户应用(tenant_mode=global):返回 "",该 system 对所有租户可见
32+
// - 单租户应用(tenant_mode=single):返回 app.tenant_id
33+
func getAppTenantID(c *gin.Context) (string, error) {
34+
mode := util.GetAppTenantMode(c)
35+
appTenantID := util.GetAppTenantID(c)
36+
37+
switch mode {
38+
case util.AppTenantModeGlobal:
39+
return "", nil
40+
case util.AppTenantModeSingle:
41+
if appTenantID == "" {
42+
return "", errors.New("single tenant app must have tenant_id in jwt")
43+
}
44+
return appTenantID, nil
45+
default:
46+
return "", fmt.Errorf(
47+
"unknown or missing tenant_mode: %q, request must come from apigateway with a valid jwt",
48+
mode,
49+
)
50+
}
51+
}
52+
2753
// add the clientID into body.Clients if not exists, 注册这个系统的client一定是其合法client!
2854
func defaultValidClients(c *gin.Context, originClients string) string {
2955
clients := originClients
@@ -85,6 +111,15 @@ func CreateSystem(c *gin.Context) {
85111
// 注册这个系统的client一定是其合法client!
86112
clients := defaultValidClients(c, body.Clients)
87113

114+
// 根据请求的App 确定 system 归属的 tenant_id
115+
// - 全租户应用: 接入系统为全租户
116+
// - 单租户应用: 接入系统为单租户,租户与应用一致
117+
tenantID, err := getAppTenantID(c)
118+
if err != nil {
119+
util.BadRequestErrorJSONResponse(c, err.Error())
120+
return
121+
}
122+
88123
// add the logical here
89124
system := svctypes.System{
90125
ID: body.ID,
@@ -94,6 +129,7 @@ func CreateSystem(c *gin.Context) {
94129
DescriptionEn: body.DescriptionEn,
95130
Clients: clients,
96131
ProviderConfig: structs.Map(body.ProviderConfig),
132+
TenantID: tenantID,
97133
}
98134

99135
svc := service.NewSystemService()

pkg/api/model/handler/system_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ func TestCreateSystem(t *testing.T) {
182182
}).
183183
Expect(t).
184184
Assert(util.NewResponseAssertFunc(t, func(resp util.Response) error {
185-
assert.Equal(t, resp.Code, util.SystemError)
186-
assert.Contains(t, resp.Message, "system error")
185+
assert.Equal(t, resp.Code, util.BadRequestError)
186+
assert.Contains(t, resp.Message, "unknown or missing tenant_mode")
187187
return nil
188188
})).
189189
Status(http.StatusOK).

pkg/api/web/handler/system_slz.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
package handler
1212

1313
const (
14-
systemSupportFields = "id,name,name_en,clients,provider_config,description,description_en"
15-
systemDefaultFields = "id,name,name_en"
14+
systemSupportFields = "id,name,name_en,clients,provider_config,description,description_en,tenant_id"
15+
systemDefaultFields = "id,name,name_en,tenant_id"
1616
)
1717

1818
type systemQuerySerializer struct {

pkg/cacheimpls/init.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var (
3737
LocalRemoteResourceListCache memory.Cache
3838
LocalSubjectPKCache memory.Cache
3939
LocalSubjectDepartmentCache memory.Cache
40-
LocalAPIGatewayJWTClientIDCache memory.Cache
40+
LocalAPIGatewayJWTAppInfoCache memory.Cache
4141
LocalActionCache memory.Cache // for iam engine
4242
LocalUnmarshaledExpressionCache *gocache.Cache
4343
LocalGroupSystemAuthTypeCache *gocache.Cache
@@ -167,10 +167,10 @@ func InitCaches(disabled bool) {
167167

168168
// 无影响, 重算而已不查db
169169

170-
LocalAPIGatewayJWTClientIDCache = memory.NewCache(
171-
"local_apigw_jwt_client_id",
170+
LocalAPIGatewayJWTAppInfoCache = memory.NewCache(
171+
"local_apigw_jwt_app_info",
172172
disabled,
173-
retrieveAPIGatewayJWTClientID,
173+
retrieveAPIGatewayJWTAppInfo,
174174
30*time.Second,
175175
nil,
176176
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available.
3+
* Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
4+
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
6+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
7+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
8+
* specific language governing permissions and limitations under the License.
9+
*/
10+
11+
package cacheimpls
12+
13+
import (
14+
"errors"
15+
16+
"github.com/TencentBlueKing/gopkg/cache"
17+
"github.com/TencentBlueKing/gopkg/stringx"
18+
)
19+
20+
// AppInfo 从网关 JWT 中解析出的应用信息
21+
type AppInfo struct {
22+
AppCode string // 应用 code
23+
TenantMode string // 租户模式:global / single
24+
TenantID string // 租户 id(global 时为空)
25+
}
26+
27+
// APIGatewayJWTAppInfoCacheKey cache key for JWTToken
28+
type APIGatewayJWTAppInfoCacheKey struct {
29+
JWTToken string
30+
}
31+
32+
// Key ...
33+
func (k APIGatewayJWTAppInfoCacheKey) Key() string {
34+
return stringx.MD5Hash(k.JWTToken)
35+
}
36+
37+
func retrieveAPIGatewayJWTAppInfo(key cache.Key) (interface{}, error) {
38+
// NOTE: this func not work
39+
return AppInfo{}, nil
40+
}
41+
42+
var (
43+
ErrAPIGatewayJWTCacheNotFound = errors.New("not found")
44+
ErrAPIGatewayJWTValueNotAppInfo = errors.New("value not AppInfo type")
45+
)
46+
47+
// GetJWTTokenAppInfo will retrieve the AppInfo of a jwtToken
48+
func GetJWTTokenAppInfo(jwtToken string) (appInfo AppInfo, err error) {
49+
key := APIGatewayJWTAppInfoCacheKey{
50+
JWTToken: jwtToken,
51+
}
52+
53+
value, ok := LocalAPIGatewayJWTAppInfoCache.DirectGet(key)
54+
if !ok {
55+
err = ErrAPIGatewayJWTCacheNotFound
56+
return
57+
}
58+
59+
appInfo, ok = value.(AppInfo)
60+
if !ok {
61+
err = ErrAPIGatewayJWTValueNotAppInfo
62+
return
63+
}
64+
return appInfo, nil
65+
}
66+
67+
// SetJWTTokenAppInfo will set the jwtToken-AppInfo in cache
68+
func SetJWTTokenAppInfo(jwtToken string, appInfo AppInfo) {
69+
key := APIGatewayJWTAppInfoCacheKey{
70+
JWTToken: jwtToken,
71+
}
72+
LocalAPIGatewayJWTAppInfoCache.Set(key, appInfo)
73+
}

pkg/cacheimpls/local_apigw_jwt_client_id_test.go renamed to pkg/cacheimpls/local_apigw_jwt_app_info_test.go

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,22 @@ import (
1919
"github.com/stretchr/testify/assert"
2020
)
2121

22-
var _ = Describe("LocalApigwJwtClientId", func() {
22+
var _ = Describe("LocalApigwJWTAppInfo", func() {
2323
It("Key", func() {
24-
key := APIGatewayJWTClientIDCacheKey{
24+
key := APIGatewayJWTAppInfoCacheKey{
2525
JWTToken: "abc",
2626
}
2727

2828
assert.Equal(GinkgoT(), "900150983cd24fb0d6963f7d28e17f72", key.Key())
2929
})
3030

3131
It("retrieve should not work", func() {
32-
key := APIGatewayJWTClientIDCacheKey{
32+
key := APIGatewayJWTAppInfoCacheKey{
3333
JWTToken: "abc",
3434
}
3535

36-
value, err := retrieveAPIGatewayJWTClientID(key)
37-
assert.Equal(GinkgoT(), "", value.(string))
36+
value, err := retrieveAPIGatewayJWTAppInfo(key)
37+
assert.Equal(GinkgoT(), AppInfo{}, value.(AppInfo))
3838
assert.NoError(GinkgoT(), err)
3939
})
4040

@@ -47,31 +47,36 @@ var _ = Describe("LocalApigwJwtClientId", func() {
4747
}
4848
mockCache := memory.NewCache(
4949
"mockCache", false, retrieveFunc, expiration, nil)
50-
LocalAPIGatewayJWTClientIDCache = mockCache
50+
LocalAPIGatewayJWTAppInfoCache = mockCache
5151
})
5252

5353
It("not exists", func() {
54-
_, err := GetJWTTokenClientID("abc")
54+
_, err := GetJWTTokenAppInfo("abc")
5555
assert.ErrorIs(GinkgoT(), err, ErrAPIGatewayJWTCacheNotFound)
5656
})
5757

58-
It("not string", func() {
59-
key := APIGatewayJWTClientIDCacheKey{
58+
It("not AppInfo", func() {
59+
key := APIGatewayJWTAppInfoCacheKey{
6060
JWTToken: "abc",
6161
}
6262

63-
LocalAPIGatewayJWTClientIDCache.Set(key, 1)
63+
LocalAPIGatewayJWTAppInfoCache.Set(key, 1)
6464

65-
_, err := GetJWTTokenClientID("abc")
66-
assert.ErrorIs(GinkgoT(), err, ErrAPIGatewayJWTClientIDNotString)
65+
_, err := GetJWTTokenAppInfo("abc")
66+
assert.ErrorIs(GinkgoT(), err, ErrAPIGatewayJWTValueNotAppInfo)
6767
})
6868

6969
It("ok", func() {
70-
SetJWTTokenClientID("abc", "bk_test")
70+
expectedAppInfo := AppInfo{
71+
AppCode: "bk_test",
72+
TenantMode: "global",
73+
TenantID: "",
74+
}
75+
SetJWTTokenAppInfo("abc", expectedAppInfo)
7176

72-
clientID, err := GetJWTTokenClientID("abc")
77+
appInfo, err := GetJWTTokenAppInfo("abc")
7378
assert.NoError(GinkgoT(), err)
74-
assert.Equal(GinkgoT(), "bk_test", clientID)
79+
assert.Equal(GinkgoT(), expectedAppInfo, appInfo)
7580
})
7681
})
7782
})

pkg/cacheimpls/local_apigw_jwt_client_id.go

Lines changed: 0 additions & 66 deletions
This file was deleted.

pkg/database/sdao/saas_system.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type SaaSSystem struct {
3333
DescriptionEn string `db:"description_en"`
3434
Clients string `db:"clients"` // 逗号分隔
3535
ProviderConfig string `db:"provider_config"` // JSON 'iam,saas_iam'
36+
TenantID string `db:"tenant_id"`
3637
}
3738

3839
// SaaSSystemManager ...
@@ -104,8 +105,9 @@ func (m *saasSystemManager) insertWithTx(tx *sqlx.Tx, system SaaSSystem) error {
104105
description,
105106
description_en,
106107
clients,
107-
provider_config
108-
) VALUES (:id, :name, :name_en, :description, :description_en, :clients, :provider_config)`
108+
provider_config,
109+
tenant_id
110+
) VALUES (:id, :name, :name_en, :description, :description_en, :clients, :provider_config, :tenant_id)`
109111
return database.SqlxInsertWithTx(tx, query, system)
110112
}
111113

@@ -125,7 +127,8 @@ func (m *saasSystemManager) selectOne(saasSystem *SaaSSystem, id string) error {
125127
description,
126128
description_en,
127129
clients,
128-
provider_config
130+
provider_config,
131+
tenant_id
129132
FROM saas_system_info
130133
WHERE id = ?
131134
LIMIT 1`
@@ -140,7 +143,8 @@ func (m *saasSystemManager) selectAll(saasSystems *[]SaaSSystem) error {
140143
description,
141144
description_en,
142145
clients,
143-
provider_config
146+
provider_config,
147+
tenant_id
144148
FROM saas_system_info ORDER BY created_at`
145149
return database.SqlxSelect(m.DB, saasSystems, query)
146150
}

0 commit comments

Comments
 (0)