mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
Merge pull request #8163 from swordqiu/hotfix/qj-oidc-auth-response-user-info
fix: oidc response user's info
This commit is contained in:
@@ -15,20 +15,17 @@
|
||||
package clientman
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncoeDecode(t *testing.T) {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
SetupTest()
|
||||
token := SAuthToken{
|
||||
token: `gAAAAABe-gUMAawOPrP-mA4jY6-b1UPalPJw9WlZJVqHZMtc3IBKUOvHTbKm60YyZQtnVBa3O3QDfS2ss5_Xwi_n0L-jfuUstguLHfDyztAvT_IAKupw8YNK0FvJg25LKC4IR3bmDzCNzTwMO-rEeb4ha2e1vkGOwko9GT1Bn-xN7UM2qeEsm5PiLBg0ZTMuv4Jm5RWIXk2K`,
|
||||
verifyTotp: true,
|
||||
enableTotp: false,
|
||||
}
|
||||
setPrivateKey(key)
|
||||
et := token.encodeBytes()
|
||||
plainEt := compressString(et)
|
||||
encEt := EncryptString(et)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package clientman
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -38,3 +40,8 @@ func InitClient() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetupTest() {
|
||||
key, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||
setPrivateKey(key)
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ func (h *AuthHandlers) AddMethods() {
|
||||
NewHP(handleOIDCAuth, "oidc", "auth"),
|
||||
NewHP(handleOIDCConfiguration, "oidc", ".well-known", "openid-configuration"),
|
||||
NewHP(handleOIDCJWKeys, "oidc", "keys"),
|
||||
NewHP(handleOIDCUserInfo, "oidc", "user"),
|
||||
)
|
||||
h.AddByMethod(POST, nil,
|
||||
NewHP(h.initTotpSecrets, "initcredential"),
|
||||
@@ -91,8 +92,6 @@ func (h *AuthHandlers) AddMethods() {
|
||||
NewHP(h.getResources, "scoped_resources"),
|
||||
NewHP(fetchIdpBasicConfig, "idp", "<idp_id>", "info"),
|
||||
NewHP(fetchIdpSAMLMetadata, "idp", "<idp_id>", "saml-metadata"),
|
||||
// oidc
|
||||
NewHP(handleOIDCUserInfo, "oidc", "user"),
|
||||
)
|
||||
h.AddByMethod(POST, FetchAuthToken,
|
||||
NewHP(h.resetUserPassword, "password"),
|
||||
@@ -251,22 +250,17 @@ func doTenantLogin(ctx context.Context, req *http.Request, body jsonutils.JSONOb
|
||||
|
||||
func fetchUserInfoFromToken(ctx context.Context, req *http.Request, token mcclient.TokenCredential) (jsonutils.JSONObject, error) {
|
||||
s := auth.GetAdminSession(ctx, FetchRegion(req), "")
|
||||
info, err := modules.UsersV3.Get(s, token.GetUserId(), nil)
|
||||
return fetchUserInfoById(s, token.GetUserId())
|
||||
}
|
||||
|
||||
func fetchUserInfoById(s *mcclient.ClientSession, userId string) (jsonutils.JSONObject, error) {
|
||||
info, err := modules.UsersV3.Get(s, userId, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "UsersV3.Get")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func FetchProjectMetadata(ctx context.Context, req *http.Request, pid string) (jsonutils.JSONObject, error) {
|
||||
s := auth.GetAdminSession(ctx, FetchRegion(req), "")
|
||||
meta, err := modules.Projects.GetSpecific(s, pid, "metadata", nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetProjectMetadata")
|
||||
}
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
func isUserEnableTotp(userInfo jsonutils.JSONObject) bool {
|
||||
return jsonutils.QueryBoolean(userInfo, "enable_mfa", false)
|
||||
}
|
||||
@@ -759,9 +753,14 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
log.Errorf("modules.UsersV3.Get fail %s", err)
|
||||
return nil, fmt.Errorf("not found user %s", token.GetUserId())
|
||||
}*/
|
||||
usr, err := fetchUserInfoFromToken(ctx, req, token)
|
||||
// usr, err := fetchUserInfoFromToken(ctx, req, token)
|
||||
return getUserInfo2(s, token.GetUserId(), token.GetProjectId(), token.GetLoginIp())
|
||||
}
|
||||
|
||||
func getUserInfo2(s *mcclient.ClientSession, uid string, pid string, loginIp string) (*jsonutils.JSONDict, error) {
|
||||
usr, err := fetchUserInfoById(s, uid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fetchUserInfoFromToken %s", token.GetUserId())
|
||||
return nil, errors.Wrapf(err, "fetchUserInfoFromToken %s", uid)
|
||||
}
|
||||
data := jsonutils.NewDict()
|
||||
for _, k := range []string{
|
||||
@@ -779,21 +778,37 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
data.Add(v, k)
|
||||
}
|
||||
}
|
||||
data.Add(jsonutils.NewString(token.GetDomainId()), "domain", "id")
|
||||
data.Add(jsonutils.NewString(token.GetDomainName()), "domain", "name")
|
||||
data.Add(jsonutils.NewStringArray(auth.AdminCredential().GetRegions()), "regions")
|
||||
data.Add(jsonutils.NewStringArray(token.GetRoles()), "roles")
|
||||
data.Add(jsonutils.NewString(token.GetProjectName()), "projectName")
|
||||
data.Add(jsonutils.NewString(token.GetProjectId()), "projectId")
|
||||
data.Add(jsonutils.NewString(token.GetProjectDomain()), "projectDomain")
|
||||
data.Add(jsonutils.NewString(token.GetProjectDomainId()), "projectDomainId")
|
||||
usrId, _ := usr.GetString("id")
|
||||
usrName, _ := usr.GetString("name")
|
||||
usrDomainId, _ := usr.GetString("domain_id")
|
||||
usrDomainName, _ := usr.GetString("project_domain")
|
||||
|
||||
data.Add(jsonutils.NewString(usrDomainId), "domain", "id")
|
||||
data.Add(jsonutils.NewString(usrDomainName), "domain", "name")
|
||||
data.Add(jsonutils.NewStringArray(auth.AdminCredential().GetRegions()), "regions")
|
||||
|
||||
var projName string
|
||||
var projDomainId string
|
||||
if len(pid) > 0 {
|
||||
projInfo, err := modules.Projects.GetById(s, pid, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fetchProjectById %s", pid)
|
||||
}
|
||||
|
||||
projName, _ = projInfo.GetString("name")
|
||||
projId, _ := projInfo.GetString("id")
|
||||
projDomainId, _ = projInfo.GetString("domain_id")
|
||||
projDomainName, _ := projInfo.GetString("project_domain")
|
||||
data.Add(jsonutils.NewString(projName), "projectName")
|
||||
data.Add(jsonutils.NewString(projId), "projectId")
|
||||
data.Add(jsonutils.NewString(projDomainName), "projectDomain")
|
||||
data.Add(jsonutils.NewString(projDomainId), "projectDomainId")
|
||||
|
||||
pmeta, err := projInfo.Get("metadata")
|
||||
if pmeta != nil {
|
||||
data.Add(pmeta, "project_meta")
|
||||
}
|
||||
|
||||
pmeta, err := FetchProjectMetadata(ctx, req, token.GetProjectId())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchProjectMetadata")
|
||||
}
|
||||
if pmeta != nil {
|
||||
data.Add(pmeta, "project_meta")
|
||||
}
|
||||
|
||||
log.Infof("getUserInfo modules.RoleAssignments.List")
|
||||
@@ -802,11 +817,12 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
query.Add(jsonutils.JSONNull, "include_names")
|
||||
query.Add(jsonutils.JSONNull, "include_system")
|
||||
query.Add(jsonutils.NewInt(0), "limit")
|
||||
query.Add(jsonutils.NewString(token.GetUserId()), "user", "id")
|
||||
query.Add(jsonutils.NewString(uid), "user", "id")
|
||||
roleAssigns, err := modules.RoleAssignments.List(s, query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get RoleAssignments list")
|
||||
}
|
||||
currentRoles := make([]string, 0)
|
||||
projects := make(map[string]*projectRoles)
|
||||
for _, roleAssign := range roleAssigns.Data {
|
||||
roleId, _ := roleAssign.GetString("role", "id")
|
||||
@@ -815,6 +831,9 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
projectName, _ := roleAssign.GetString("scope", "project", "name")
|
||||
domainId, _ := roleAssign.GetString("scope", "project", "domain", "id")
|
||||
domain, _ := roleAssign.GetString("scope", "project", "domain", "name")
|
||||
if projectId == pid {
|
||||
currentRoles = append(currentRoles, roleName)
|
||||
}
|
||||
_, ok := projects[projectId]
|
||||
if ok {
|
||||
projects[projectId].add(roleId, roleName)
|
||||
@@ -822,31 +841,38 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
projects[projectId] = newProjectRoles(projectId, projectName, roleId, roleName, domainId, domain)
|
||||
}
|
||||
}
|
||||
|
||||
data.Add(jsonutils.NewStringArray(currentRoles), "roles")
|
||||
|
||||
projJson := jsonutils.NewArray()
|
||||
for _, proj := range projects {
|
||||
projJson.Add(proj.json(
|
||||
token.GetUserName(),
|
||||
token.GetUserId(),
|
||||
token.GetDomainName(),
|
||||
token.GetDomainId(),
|
||||
token.GetLoginIp(),
|
||||
usrName,
|
||||
usrId,
|
||||
usrDomainName,
|
||||
usrDomainId,
|
||||
loginIp,
|
||||
))
|
||||
}
|
||||
data.Add(projJson, "projects")
|
||||
|
||||
for _, scope := range []rbacutils.TRbacScope{
|
||||
rbacutils.ScopeSystem,
|
||||
rbacutils.ScopeDomain,
|
||||
rbacutils.ScopeProject,
|
||||
} {
|
||||
p := policy.PolicyManager.MatchedPolicyNames(scope, token)
|
||||
data.Add(jsonutils.NewStringArray(p), fmt.Sprintf("%s_policies", scope))
|
||||
if scope == rbacutils.ScopeSystem {
|
||||
data.Add(jsonutils.NewStringArray(p), "admin_policies")
|
||||
} else if scope == rbacutils.ScopeProject {
|
||||
data.Add(jsonutils.NewStringArray(p), "policies")
|
||||
if len(pid) > 0 {
|
||||
ident := rbacutils.NewRbacIdentity2(projDomainId, projName, currentRoles, loginIp)
|
||||
for _, scope := range []rbacutils.TRbacScope{
|
||||
rbacutils.ScopeSystem,
|
||||
rbacutils.ScopeDomain,
|
||||
rbacutils.ScopeProject,
|
||||
} {
|
||||
p := policy.PolicyManager.MatchedPolicyNames(scope, ident)
|
||||
data.Add(jsonutils.NewStringArray(p), fmt.Sprintf("%s_policies", scope))
|
||||
if scope == rbacutils.ScopeSystem {
|
||||
data.Add(jsonutils.NewStringArray(p), "admin_policies")
|
||||
} else if scope == rbacutils.ScopeProject {
|
||||
data.Add(jsonutils.NewStringArray(p), "policies")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allPolicies := policy.PolicyManager.AllPolicies()
|
||||
data.Add(jsonutils.Marshal(allPolicies), "all_policies")
|
||||
|
||||
@@ -854,7 +880,7 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
menus := jsonutils.NewArray()
|
||||
k8s := jsonutils.NewArray()
|
||||
|
||||
curReg := FetchRegion(req)
|
||||
curReg := s.GetRegion()
|
||||
srvCat := auth.Client().GetServiceCatalog()
|
||||
var allsrv []string
|
||||
var alleps []mcclient.ExternalService
|
||||
@@ -908,13 +934,13 @@ func getUserInfo(ctx context.Context, req *http.Request) (*jsonutils.JSONDict, e
|
||||
}
|
||||
|
||||
log.Infof("getUserInfo modules.Hosts.Get")
|
||||
s2 := auth.GetSession(ctx, token, FetchRegion(req), "v2")
|
||||
// s2 := auth.GetSession(ctx, token, FetchRegion(req), "v2")
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString("host_type"), "field")
|
||||
params.Add(jsonutils.NewString("system"), "scope")
|
||||
params.Add(jsonutils.JSONTrue, "usable")
|
||||
params.Add(jsonutils.JSONTrue, "show_emulated")
|
||||
cap, err := modules.Hosts.Get(s2, "distinct-field", params)
|
||||
cap, err := modules.Hosts.Get(s, "distinct-field", params)
|
||||
if err != nil {
|
||||
log.Errorf("modules.Servers.Get distinct-field fail %s", err)
|
||||
} else {
|
||||
|
||||
@@ -45,7 +45,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// OIDC code expires in 5 minutes
|
||||
OIDC_CODE_EXPIRE_SECONDS = 300
|
||||
// OIDC token expires in 2 hours
|
||||
OIDC_TOKEN_EXPIRE_SECONDS = 7200
|
||||
)
|
||||
|
||||
func getLoginCallbackParam() string {
|
||||
@@ -72,7 +75,7 @@ func addQuery(urlstr string, qs jsonutils.JSONObject) string {
|
||||
func handleOIDCAuth(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
ctx, err := fetchAndSetAuthContext(ctx, w, req)
|
||||
if err != nil {
|
||||
// redirect to login page
|
||||
// not login redirect to login page
|
||||
qs := jsonutils.NewDict()
|
||||
oUrl := req.URL.String()
|
||||
if !strings.HasPrefix(oUrl, "http") {
|
||||
@@ -135,8 +138,10 @@ func doOIDCAuth(ctx context.Context, req *http.Request, query jsonutils.JSONObje
|
||||
return oidcAuth, "", errors.Wrap(httperrors.ErrInvalidCredential, "redirect uri not match")
|
||||
}
|
||||
|
||||
token := AppContextToken(ctx)
|
||||
|
||||
cliIp := netutils2.GetHttpRequestIp(req)
|
||||
codeInfo := newOIDCClientInfo(cliIp)
|
||||
codeInfo := newOIDCClientInfo(token, cliIp, FetchRegion(req))
|
||||
code := clientman.EncryptString(codeInfo.toBytes())
|
||||
|
||||
return oidcAuth, code, nil
|
||||
@@ -155,12 +160,20 @@ func handleOIDCToken(ctx context.Context, w http.ResponseWriter, req *http.Reque
|
||||
type SOIDCClientInfo struct {
|
||||
Timestamp int64
|
||||
Ip netutils.IPV4Addr
|
||||
UserId string
|
||||
ProjectId string
|
||||
Region string
|
||||
}
|
||||
|
||||
func (i SOIDCClientInfo) toBytes() []byte {
|
||||
enc := make([]byte, 12)
|
||||
enc := make([]byte, 12+1+len(i.UserId)+1+len(i.ProjectId)+len(i.Region))
|
||||
binary.LittleEndian.PutUint64(enc, uint64(i.Timestamp))
|
||||
binary.LittleEndian.PutUint32(enc[8:], uint32(i.Ip))
|
||||
enc[12] = byte(len(i.UserId))
|
||||
enc[13] = byte(len(i.ProjectId))
|
||||
copy(enc[14:], i.UserId)
|
||||
copy(enc[14+len(i.UserId):], i.ProjectId)
|
||||
copy(enc[14+len(i.UserId)+len(i.ProjectId):], i.Region)
|
||||
return enc
|
||||
}
|
||||
|
||||
@@ -171,23 +184,67 @@ func (i SOIDCClientInfo) isExpired() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (i SOIDCClientInfo) expiresAt(secs int) time.Time {
|
||||
expires := i.Timestamp + int64(secs)*int64(time.Second)
|
||||
esecs := expires / int64(time.Second)
|
||||
nsecs := expires - esecs*int64(time.Second)
|
||||
return time.Unix(esecs, nsecs)
|
||||
}
|
||||
|
||||
func decodeOIDCClientInfo(enc []byte) (SOIDCClientInfo, error) {
|
||||
info := SOIDCClientInfo{}
|
||||
if len(enc) != 8+4 {
|
||||
if len(enc) < 8+4+1 {
|
||||
return info, errors.Wrap(httperrors.ErrInvalidCredential, "code byte length must be 12")
|
||||
}
|
||||
info.Timestamp = int64(binary.LittleEndian.Uint64(enc))
|
||||
info.Ip = netutils.IPV4Addr(binary.LittleEndian.Uint32(enc[8:]))
|
||||
info.UserId = string(enc[14 : 14+int(enc[12])])
|
||||
info.ProjectId = string(enc[14+int(enc[12]) : 14+int(enc[12])+int(enc[13])])
|
||||
info.Region = string(enc[14+int(enc[12])+int(enc[13]):])
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func newOIDCClientInfo(ipstr string) SOIDCClientInfo {
|
||||
func newOIDCClientInfo(token mcclient.TokenCredential, ipstr string, region string) SOIDCClientInfo {
|
||||
info := SOIDCClientInfo{}
|
||||
info.Timestamp = time.Now().UnixNano()
|
||||
info.Ip, _ = netutils.NewIPV4Addr(ipstr)
|
||||
info.UserId = token.GetUserId()
|
||||
info.ProjectId = token.GetProjectId()
|
||||
info.Region = region
|
||||
return info
|
||||
}
|
||||
|
||||
type SOIDCClientToken struct {
|
||||
Info SOIDCClientInfo
|
||||
}
|
||||
|
||||
func (t SOIDCClientToken) encode() string {
|
||||
json := jsonutils.NewDict()
|
||||
json.Add(jsonutils.NewString(string(t.Info.toBytes())), "info")
|
||||
return clientman.EncryptString([]byte(json.String()))
|
||||
}
|
||||
|
||||
func decodeOIDCClientToken(token string) (SOIDCClientToken, error) {
|
||||
ret := SOIDCClientToken{}
|
||||
tBytes, err := clientman.DecryptString(token)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "DecryptString")
|
||||
}
|
||||
json, err := jsonutils.Parse(tBytes)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "json.Parse")
|
||||
}
|
||||
info, err := json.GetString("info")
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "getString(info)")
|
||||
}
|
||||
ret.Info, err = decodeOIDCClientInfo([]byte(info))
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "decodeOIDCClientInfo")
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func validateOIDCToken(ctx context.Context, req *http.Request) (oidcutils.SOIDCAccessTokenResponse, error) {
|
||||
var tokenResp oidcutils.SOIDCAccessTokenResponse
|
||||
bodyBytes, err := appsrv.Fetch(req)
|
||||
@@ -253,35 +310,29 @@ func validateOIDCToken(ctx context.Context, req *http.Request) (oidcutils.SOIDCA
|
||||
return tokenResp, errors.Wrap(httperrors.ErrInvalidCredential, "client secret not match")
|
||||
}
|
||||
|
||||
token, err := auth.Client().AuthenticateByAccessKey(clientId, clientSecret, codeInfo.Ip.String())
|
||||
if err != nil {
|
||||
return tokenResp, errors.Wrap(err, "invalid client_id/client_secret")
|
||||
token := SOIDCClientToken{
|
||||
Info: codeInfo,
|
||||
}
|
||||
|
||||
tokenResp = token2AccessTokenResponse(token, clientId)
|
||||
return tokenResp, nil
|
||||
}
|
||||
|
||||
func token2AccessTokenResponse(token mcclient.TokenCredential, clientId string) oidcutils.SOIDCAccessTokenResponse {
|
||||
func token2AccessTokenResponse(token SOIDCClientToken, clientId string) oidcutils.SOIDCAccessTokenResponse {
|
||||
resp := oidcutils.SOIDCAccessTokenResponse{}
|
||||
resp.AccessToken = token2AccessToken(token)
|
||||
resp.AccessToken = token.encode()
|
||||
resp.TokenType = oidcutils.OIDC_BEARER_TOKEN_TYPE
|
||||
resp.IdToken, _ = token2IdToken(token, clientId)
|
||||
resp.ExpiresIn = int(token.GetExpires().Unix() - time.Now().Unix())
|
||||
resp.ExpiresIn = int(token.Info.expiresAt(OIDC_TOKEN_EXPIRE_SECONDS).Unix() - time.Now().Unix())
|
||||
return resp
|
||||
}
|
||||
|
||||
func token2AccessToken(token mcclient.TokenCredential) string {
|
||||
authToken := clientman.NewAuthToken(token.GetTokenString(), false, false)
|
||||
return authToken.Encode()
|
||||
}
|
||||
|
||||
func token2IdToken(token mcclient.TokenCredential, clientId string) (string, error) {
|
||||
func token2IdToken(token SOIDCClientToken, clientId string) (string, error) {
|
||||
jwtToken := jwt.New()
|
||||
jwtToken.Set(jwt.IssuerKey, options.Options.ApiServer)
|
||||
jwtToken.Set(jwt.SubjectKey, token.GetUserId())
|
||||
jwtToken.Set(jwt.SubjectKey, token.Info.UserId)
|
||||
jwtToken.Set(jwt.AudienceKey, clientId)
|
||||
jwtToken.Set(jwt.ExpirationKey, token.GetExpires().Unix())
|
||||
jwtToken.Set(jwt.ExpirationKey, token.Info.expiresAt(OIDC_TOKEN_EXPIRE_SECONDS).Unix())
|
||||
jwtToken.Set(jwt.IssuedAtKey, time.Now().Unix())
|
||||
return clientman.SignJWT(jwtToken)
|
||||
}
|
||||
@@ -334,7 +385,24 @@ func handleOIDCJWKeys(ctx context.Context, w http.ResponseWriter, req *http.Requ
|
||||
}
|
||||
|
||||
func handleOIDCUserInfo(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
data, err := getUserInfo(ctx, req)
|
||||
tokenHdr := getAuthToken(req)
|
||||
if len(tokenHdr) == 0 {
|
||||
httperrors.InvalidCredentialError(ctx, w, "No token in header")
|
||||
return
|
||||
}
|
||||
token, err := decodeOIDCClientToken(tokenHdr)
|
||||
if err != nil {
|
||||
log.Errorf("decodeOIDCClientToken %s fail %s", tokenHdr, err)
|
||||
httperrors.InvalidCredentialError(ctx, w, "Token in header invalid")
|
||||
return
|
||||
}
|
||||
if token.Info.expiresAt(OIDC_TOKEN_EXPIRE_SECONDS).Before(time.Now()) {
|
||||
httperrors.InvalidCredentialError(ctx, w, "Token expired")
|
||||
return
|
||||
}
|
||||
|
||||
s := auth.GetAdminSession(ctx, token.Info.Region, "")
|
||||
data, err := getUserInfo2(s, token.Info.UserId, token.Info.ProjectId, token.Info.Ip.String())
|
||||
if err != nil {
|
||||
httperrors.NotFoundError(ctx, w, "%v", err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apigateway/clientman"
|
||||
)
|
||||
|
||||
func TestClientInfo(t *testing.T) {
|
||||
clientman.SetupTest()
|
||||
|
||||
cases := []struct {
|
||||
ip string
|
||||
user string
|
||||
project string
|
||||
}{
|
||||
{
|
||||
ip: "0.0.0.0",
|
||||
user: "sysadmin",
|
||||
project: "system",
|
||||
},
|
||||
{
|
||||
ip: "10.168.26.253",
|
||||
user: "ab9502de-c6b6-4150-880b-d0e3e6ba8ec8",
|
||||
project: "a2049cfadf4c40888b9da136faba5cc8",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
info := SOIDCClientInfo{}
|
||||
info.Timestamp = time.Now().UnixNano()
|
||||
info.Ip, _ = netutils.NewIPV4Addr(c.ip)
|
||||
info.UserId = c.user
|
||||
info.ProjectId = c.project
|
||||
|
||||
msg := info.toBytes()
|
||||
if len(msg) != 14+len(info.UserId)+len(info.ProjectId)+len(info.Region) {
|
||||
t.Fatalf("incorrect msg size")
|
||||
}
|
||||
|
||||
info2, err := decodeOIDCClientInfo(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("decode error %s", err)
|
||||
}
|
||||
|
||||
if info2.Timestamp != info.Timestamp {
|
||||
t.Fatalf("incorrect timestamp")
|
||||
}
|
||||
if info2.Ip.String() != info.Ip.String() {
|
||||
t.Fatalf("incorrect ip")
|
||||
}
|
||||
if info2.UserId != info.UserId {
|
||||
t.Fatalf("incorrect user id")
|
||||
}
|
||||
if info2.ProjectId != info.ProjectId {
|
||||
t.Fatalf("incorrect project id")
|
||||
}
|
||||
if info2.Region != info.Region {
|
||||
t.Fatalf("incorrect region id")
|
||||
}
|
||||
|
||||
token := SOIDCClientToken{
|
||||
Info: info,
|
||||
}
|
||||
|
||||
tokenStr := token.encode()
|
||||
token2, err := decodeOIDCClientToken(tokenStr)
|
||||
if err != nil {
|
||||
t.Fatalf("decodeOIDCClientToken fail %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(token2.Info, info2) {
|
||||
t.Fatalf("token2 info not equal to info2")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,6 +655,7 @@ type sSimpleRbacIdentity struct {
|
||||
domainId string
|
||||
projectName string
|
||||
roleNames []string
|
||||
loginIp string
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetProjectDomainId() string {
|
||||
@@ -670,7 +671,7 @@ func (id sSimpleRbacIdentity) GetProjectName() string {
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetLoginIp() string {
|
||||
return ""
|
||||
return id.loginIp
|
||||
}
|
||||
|
||||
func (id sSimpleRbacIdentity) GetTokenString() string {
|
||||
@@ -678,10 +679,15 @@ func (id sSimpleRbacIdentity) GetTokenString() string {
|
||||
}
|
||||
|
||||
func NewRbacIdentity(domainId, projectName string, roleNames []string) IRbacIdentity {
|
||||
return NewRbacIdentity2(domainId, projectName, roleNames, "")
|
||||
}
|
||||
|
||||
func NewRbacIdentity2(domainId, projectName string, roleNames []string, loginIp string) IRbacIdentity {
|
||||
return sSimpleRbacIdentity{
|
||||
domainId: domainId,
|
||||
projectName: projectName,
|
||||
roleNames: roleNames,
|
||||
loginIp: loginIp,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user