mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
feature: invalidate auth token
This commit is contained in:
@@ -656,8 +656,14 @@ func doLogout(ctx context.Context, w http.ResponseWriter, req *http.Request) {
|
||||
token, _, _ := fetchAuthInfo(ctx, req)
|
||||
if token != nil {
|
||||
// valid login, log the event
|
||||
user := logclient.NewSimpleObject(token.GetUserId(), token.GetUserName(), "user")
|
||||
logclient.AddActionLogWithContext(ctx, user, logclient.ACT_LOGOUT, "", token, true)
|
||||
err := auth.Remove(ctx, token.GetTokenString())
|
||||
if err != nil {
|
||||
log.Errorf("remove token fail %s", err)
|
||||
return
|
||||
} else {
|
||||
user := logclient.NewSimpleObject(token.GetUserId(), token.GetUserName(), "user")
|
||||
logclient.AddActionLogWithContext(ctx, user, logclient.ACT_LOGOUT, "", token, true)
|
||||
}
|
||||
}
|
||||
clearAuthCookie(w)
|
||||
appsrv.DisableClientCache(w)
|
||||
|
||||
@@ -39,4 +39,6 @@ type SUserExtended struct {
|
||||
IsLocal bool
|
||||
// IdpId string
|
||||
// IdpName string
|
||||
|
||||
AuditIds []string
|
||||
}
|
||||
|
||||
@@ -163,6 +163,10 @@ func (self *SCASDriver) Authenticate(ctx context.Context, ident mcclient.SAuthen
|
||||
|
||||
idp.TryUserJoinProject(self.casConfig.SIdpAttributeOptions, ctx, usr, domain.Id, attrs)
|
||||
|
||||
extUser.AuditIds = []string{
|
||||
ident.CASTicket.Id,
|
||||
}
|
||||
|
||||
return extUser, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -308,5 +308,7 @@ func (drv *SLDAPDriver) Authenticate(ctx context.Context, ident mcclient.SAuthen
|
||||
return nil, errors.Wrap(err, "Authenticate error")
|
||||
}
|
||||
|
||||
usrExt.AuditIds = []string{username}
|
||||
|
||||
return usrExt, nil
|
||||
}
|
||||
|
||||
@@ -118,6 +118,8 @@ func (self *SOAuth2Driver) Authenticate(ctx context.Context, ident mcclient.SAut
|
||||
|
||||
idp.TryUserJoinProject(options, ctx, usr, domain.Id, attrs)
|
||||
|
||||
extUser.AuditIds = []string{ident.OAuth2.Code}
|
||||
|
||||
return extUser, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +188,8 @@ func (self *SOIDCDriver) Authenticate(ctx context.Context, ident mcclient.SAuthe
|
||||
|
||||
idp.TryUserJoinProject(self.oidcConfig.SIdpAttributeOptions, ctx, usr, domain.Id, attrs)
|
||||
|
||||
extUser.AuditIds = []string{ident.OIDCAuth.Code}
|
||||
|
||||
return extUser, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -157,6 +157,8 @@ func (self *SSAMLDriver) Authenticate(ctx context.Context, ident mcclient.SAuthe
|
||||
|
||||
idp.TryUserJoinProject(self.samlConfig.SIdpAttributeOptions, ctx, usr, domain.Id, attrs)
|
||||
|
||||
extUser.AuditIds = []string{resp.ID}
|
||||
|
||||
return extUser, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -81,6 +82,8 @@ func (sql *SSQLDriver) Authenticate(ctx context.Context, ident mcclient.SAuthent
|
||||
return nil, errors.Wrap(err, "usrExt.VerifyPassword")
|
||||
}
|
||||
localUser.ClearFailedAuth()
|
||||
|
||||
usrExt.AuditIds = []string{fmt.Sprintf("%d", localUser.Id)}
|
||||
return usrExt, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
@@ -161,14 +162,14 @@ func (manager *SCredentialManager) ValidateCreateData(
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (self *SCredential) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
||||
return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
|
||||
func (cred *SCredential) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
|
||||
return cred.SStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *SCredential) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CredentialUpdateInput) (api.CredentialUpdateInput, error) {
|
||||
func (cred *SCredential) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CredentialUpdateInput) (api.CredentialUpdateInput, error) {
|
||||
var err error
|
||||
|
||||
input.StandaloneResourceBaseUpdateInput, err = self.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput)
|
||||
input.StandaloneResourceBaseUpdateInput, err = cred.SStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneResourceBaseUpdateInput)
|
||||
if err != nil {
|
||||
return input, errors.Wrap(err, "SStandaloneResourceBase.ValidateUpdateData")
|
||||
}
|
||||
@@ -210,13 +211,13 @@ func credentialExtra(cred *SCredential, out api.CredentialDetails) api.Credentia
|
||||
return out
|
||||
}
|
||||
|
||||
func (self *SCredential) getBlob() []byte {
|
||||
return keys.CredentialKeyManager.Decrypt([]byte(self.EncryptedBlob))
|
||||
func (cred *SCredential) getBlob() []byte {
|
||||
return keys.CredentialKeyManager.Decrypt([]byte(cred.EncryptedBlob))
|
||||
}
|
||||
|
||||
func (self *SCredential) GetAccessKeySecret() (*api.SAccessKeySecretBlob, error) {
|
||||
if self.Type == api.ACCESS_SECRET_TYPE || self.Type == api.OIDC_CREDENTIAL_TYPE {
|
||||
blobJson, err := jsonutils.Parse(self.getBlob())
|
||||
func (cred *SCredential) GetAccessKeySecret() (*api.SAccessKeySecretBlob, error) {
|
||||
if cred.Type == api.ACCESS_SECRET_TYPE || cred.Type == api.OIDC_CREDENTIAL_TYPE {
|
||||
blobJson, err := jsonutils.Parse(cred.getBlob())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "jsonutils.Parse")
|
||||
}
|
||||
@@ -245,8 +246,8 @@ func (manager *SCredentialManager) FilterByOwner(q *sqlchemy.SQuery, man db.Filt
|
||||
return q
|
||||
}
|
||||
|
||||
func (self *SCredential) GetOwnerId() mcclient.IIdentityProvider {
|
||||
owner := db.SOwnerId{UserId: self.UserId}
|
||||
func (cred *SCredential) GetOwnerId() mcclient.IIdentityProvider {
|
||||
owner := db.SOwnerId{UserId: cred.UserId}
|
||||
return &owner
|
||||
}
|
||||
|
||||
@@ -363,3 +364,20 @@ func (manager *SCredentialManager) DeleteAll(ctx context.Context, userCred mccli
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cred *SCredential) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
err := cred.SStandaloneResourceBase.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "SStandaloneResourceBase.Delete")
|
||||
}
|
||||
|
||||
if cred.Type == api.ACCESS_SECRET_TYPE {
|
||||
// clean tokens auth by this AKSK
|
||||
err := TokenCacheManager.BatchInvalidate(ctx, api.AUTH_METHOD_AKSK, []string{cred.Id})
|
||||
if err != nil {
|
||||
log.Errorf("BatchInvalidate token failed %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// 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 models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/timeutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
|
||||
var TokenCacheManager *STokenCacheManager
|
||||
|
||||
func init() {
|
||||
TokenCacheManager = &STokenCacheManager{
|
||||
SModelBaseManager: db.NewModelBaseManager(
|
||||
STokenCache{},
|
||||
"token_cache_tbl",
|
||||
"token_cache",
|
||||
"token_caches",
|
||||
),
|
||||
}
|
||||
TokenCacheManager.SetVirtualObject(TokenCacheManager)
|
||||
}
|
||||
|
||||
type STokenCache struct {
|
||||
db.SModelBase
|
||||
|
||||
Token string `width:"700" charset:"ascii" nullable:"false" primary:"true"`
|
||||
ExpiredAt time.Time `nullable:"false"`
|
||||
Valid bool
|
||||
Method string `width:"32" charset:"ascii"`
|
||||
AuditIds string `width:"700" charset:"utf8" index:"true"`
|
||||
}
|
||||
|
||||
type STokenCacheManager struct {
|
||||
db.SModelBaseManager
|
||||
}
|
||||
|
||||
func joinAuditIds(ids []string) string {
|
||||
sort.Strings(ids)
|
||||
return strings.Join(ids, ",")
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) Save(ctx context.Context, token string, expiredAt time.Time, method string, auditIds []string) error {
|
||||
return manager.insert(ctx, token, expiredAt, true, method, auditIds)
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) Invalidate(ctx context.Context, token string, expiredAt time.Time, method string, auditIds []string) error {
|
||||
return manager.insert(ctx, token, expiredAt, false, method, auditIds)
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) BatchInvalidate(ctx context.Context, method string, auditIds []string) error {
|
||||
invalidQueue := []sCacheCredential{
|
||||
{
|
||||
Method: method,
|
||||
AuditIds: auditIds,
|
||||
},
|
||||
}
|
||||
for i := 0; i < len(invalidQueue); i++ {
|
||||
queues, err := manager.batchInvalidateInternal(ctx, invalidQueue[i])
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "batchInvalidateInternal")
|
||||
}
|
||||
if len(queues) > 0 {
|
||||
invalidQueue = append(invalidQueue, queues...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sCacheCredential struct {
|
||||
Method string
|
||||
AuditIds []string
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) batchInvalidateInternal(ctx context.Context, cred sCacheCredential) ([]sCacheCredential, error) {
|
||||
q := manager.Query().Equals("method", cred.Method).Equals("audit_ids", joinAuditIds(cred.AuditIds))
|
||||
tokens := make([]STokenCache, 0)
|
||||
err := db.FetchModelObjects(manager, q, &tokens)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
if len(tokens) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
queues := make([]sCacheCredential, 0)
|
||||
for i := range tokens {
|
||||
token := tokens[i]
|
||||
queues = append(queues, sCacheCredential{
|
||||
Method: api.AUTH_METHOD_TOKEN,
|
||||
AuditIds: []string{token.Token},
|
||||
})
|
||||
}
|
||||
err = manager.TableSpec().GetTableSpec().UpdateBatch(
|
||||
map[string]interface{}{
|
||||
"valid": false,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"method": cred.Method,
|
||||
"audit_ids": joinAuditIds(cred.AuditIds),
|
||||
},
|
||||
)
|
||||
return queues, errors.Wrap(err, "UpdateBatch")
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) insert(ctx context.Context, token string, expiredAt time.Time, valid bool, method string, auditIds []string) error {
|
||||
val := STokenCache{
|
||||
Token: token,
|
||||
ExpiredAt: expiredAt,
|
||||
Valid: valid,
|
||||
Method: method,
|
||||
AuditIds: joinAuditIds(auditIds),
|
||||
}
|
||||
err := manager.TableSpec().InsertOrUpdate(ctx, &val)
|
||||
return errors.Wrap(err, "InsertOrUpdate")
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) IsValid(token string) (bool, error) {
|
||||
q := manager.Query().Equals("token", token)
|
||||
tokenCache := STokenCache{}
|
||||
err := q.First(&tokenCache)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "Query")
|
||||
}
|
||||
return tokenCache.Valid, nil
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) removeObsolete() error {
|
||||
sql := fmt.Sprintf("DELETE FROM `%s` WHERE `expired_at` < ?", manager.TableSpec().Name())
|
||||
db := sqlchemy.GetDBWithName(manager.TableSpec().GetDBName())
|
||||
now := timeutils.UtcNow()
|
||||
_, err := db.Exec(sql, now.Add(-24*time.Hour))
|
||||
return errors.Wrap(err, "Exec Delete")
|
||||
}
|
||||
|
||||
func RemoveObsoleteInvalidTokens(ctx context.Context, userCred mcclient.TokenCredential, start bool) {
|
||||
err := TokenCacheManager.removeObsolete()
|
||||
if err != nil {
|
||||
log.Errorf("RemoveObsoleteInvalidTokens fail %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (manager *STokenCacheManager) FetchInvalidTokens() ([]string, error) {
|
||||
q := manager.Query("token").IsFalse("valid")
|
||||
tokens := make([]STokenCache, 0)
|
||||
err := db.FetchModelObjects(manager, q, &tokens)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchModelObjects")
|
||||
}
|
||||
ret := make([]string, len(tokens))
|
||||
for i := range tokens {
|
||||
ret[i] = tokens[i].Token
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -929,6 +930,10 @@ func (user *SUser) Delete(ctx context.Context, userCred mcclient.TokenCredential
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "PasswordManager.delete")
|
||||
}
|
||||
batchErr := TokenCacheManager.BatchInvalidate(ctx, api.AUTH_METHOD_PASSWORD, []string{fmt.Sprintf("%d", localUser.Id)})
|
||||
if batchErr != nil {
|
||||
log.Errorf("BatchInvalidate fail %s", batchErr)
|
||||
}
|
||||
}
|
||||
|
||||
err = IdmappingManager.deleteByPublicId(user.Id, api.IdMappingEntityUser)
|
||||
|
||||
@@ -75,6 +75,8 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.IdentityQuotaManager,
|
||||
models.IdentityUsageManager,
|
||||
models.IdentityPendingUsageManager,
|
||||
|
||||
models.TokenCacheManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
|
||||
@@ -100,6 +100,8 @@ func StartService() {
|
||||
cron.AddJobEveryFewHour("AutoPurgeSplitable", 4, 30, 0, db.AutoPurgeSplitable, false)
|
||||
cron.AddJobEveryFewDays("CheckAllUserPasswordIsExpired", 1, 8, 0, 0, models.CheckAllUserPasswordIsExpired, true)
|
||||
|
||||
cron.AddJobEveryFewHour("RemoveObsoleteInvalidTokens", 6, 0, 0, models.RemoveObsoleteInvalidTokens, true)
|
||||
|
||||
cron.Start()
|
||||
defer cron.Stop()
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/s3auth"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
@@ -47,12 +46,32 @@ func authUserByTokenV3(ctx context.Context, input mcclient.SAuthenticationInputV
|
||||
}
|
||||
|
||||
func authUserByToken(ctx context.Context, tokenStr string) (*api.SUserExtended, error) {
|
||||
valid, err := models.TokenCacheManager.IsValid(tokenStr)
|
||||
if err == nil {
|
||||
if !valid {
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidCredential, "invalid token")
|
||||
} else {
|
||||
// passthrough
|
||||
}
|
||||
} else {
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "TokenCacheManager.IsValid")
|
||||
} else {
|
||||
// passthrough
|
||||
}
|
||||
}
|
||||
|
||||
token := SAuthToken{}
|
||||
err := token.ParseFernetToken(tokenStr)
|
||||
err = token.ParseFernetToken(tokenStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "token.ParseFernetToken")
|
||||
}
|
||||
return models.UserManager.FetchUserExtended(token.UserId, "", "", "")
|
||||
extUser, err := models.UserManager.FetchUserExtended(token.UserId, "", "", "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "FetchUserExtended")
|
||||
}
|
||||
extUser.AuditIds = []string{tokenStr}
|
||||
return extUser, nil
|
||||
}
|
||||
|
||||
func authUserByPasswordV2(ctx context.Context, input mcclient.SAuthenticationInputV2) (*api.SUserExtended, error) {
|
||||
@@ -390,6 +409,9 @@ func authUserByAccessKeyV3(ctx context.Context, input mcclient.SAuthenticationIn
|
||||
if err != nil {
|
||||
return nil, "", aksk, errors.Wrap(err, "UserManager.FetchUserExtended")
|
||||
}
|
||||
|
||||
usrExt.AuditIds = []string{keyId}
|
||||
|
||||
return usrExt, credential.ProjectId, aksk, nil
|
||||
}
|
||||
|
||||
@@ -410,6 +432,9 @@ func authUserByVerify(ctx context.Context, input mcclient.SAuthenticationInputV3
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Verify")
|
||||
}
|
||||
|
||||
extUser.AuditIds = []string{input.Auth.Identity.Verify.Uid}
|
||||
|
||||
return extUser, nil
|
||||
}
|
||||
|
||||
@@ -496,7 +521,7 @@ func AuthenticateV3(ctx context.Context, input mcclient.SAuthenticationInputV3)
|
||||
token := SAuthToken{}
|
||||
token.UserId = user.Id
|
||||
token.Method = method
|
||||
token.AuditIds = []string{utils.GenRequestId(16)}
|
||||
token.AuditIds = user.AuditIds
|
||||
now := time.Now().UTC()
|
||||
token.ExpiresAt = now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
|
||||
token.Context = input.Auth.Context
|
||||
@@ -540,6 +565,9 @@ func AuthenticateV3(ctx context.Context, input mcclient.SAuthenticationInputV3)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getTokenV3")
|
||||
}
|
||||
|
||||
models.TokenCacheManager.Save(ctx, tokenV3.Id, token.ExpiresAt, token.Method, token.AuditIds)
|
||||
|
||||
return tokenV3, nil
|
||||
}
|
||||
|
||||
@@ -600,7 +628,7 @@ func _authenticateV2(ctx context.Context, input mcclient.SAuthenticationInputV2)
|
||||
token := SAuthToken{}
|
||||
token.UserId = user.Id
|
||||
token.Method = method
|
||||
token.AuditIds = []string{utils.GenRequestId(16)}
|
||||
token.AuditIds = user.AuditIds
|
||||
now := time.Now().UTC()
|
||||
token.ExpiresAt = now.Add(time.Duration(options.Options.TokenExpirationSeconds) * time.Second)
|
||||
token.Context = input.Auth.Context
|
||||
@@ -625,5 +653,12 @@ func _authenticateV2(ctx context.Context, input mcclient.SAuthenticationInputV2)
|
||||
return nil, errors.Wrap(err, "project.FetchExtend")
|
||||
}
|
||||
|
||||
return token.getTokenV2(ctx, user, projExt)
|
||||
tokenV2, err := token.getTokenV2(ctx, user, projExt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "getTokenV2")
|
||||
}
|
||||
|
||||
models.TokenCacheManager.Save(ctx, tokenV2.Token.Id, token.ExpiresAt, token.Method, token.AuditIds)
|
||||
|
||||
return tokenV2, nil
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package tokens
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"net/http"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -40,6 +41,9 @@ func AddHandler(app *appsrv.Application) {
|
||||
app.AddHandler2("GET", "/v3/auth/tokens", authenticateToken(verifyTokensV3), nil, "verify_tokens_v3", nil)
|
||||
app.AddHandler2("GET", "/v3/auth/policies", authenticateToken(fetchTokenPolicies), nil, "fetch_token_policies", nil)
|
||||
app.AddHandler2("POST", "/v3/auth/policies", authenticateToken(postTokenPolicies), nil, "post_token_policies", nil)
|
||||
|
||||
app.AddHandler2("DELETE", "/v3/auth/tokens", authenticateToken(invalidateTokenV3), nil, "delete_tokens_v3", nil)
|
||||
app.AddHandler2("GET", "/v3/auth/tokens/invalid", authenticateToken(fetchInvalidTokensV3), nil, "fetch_revoked_tokens_v3", nil)
|
||||
}
|
||||
|
||||
func FetchAuthContext(authCtx mcclient.SAuthContext, r *http.Request) mcclient.SAuthContext {
|
||||
@@ -131,6 +135,24 @@ type VerifyTokenV2Param struct {
|
||||
func verifyTokensV2(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params, _, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
tokenStr := params["<token>"]
|
||||
|
||||
valid, err := models.TokenCacheManager.IsValid(tokenStr)
|
||||
if err == nil {
|
||||
if !valid {
|
||||
httperrors.InvalidCredentialError(ctx, w, "invalid token")
|
||||
return
|
||||
} else {
|
||||
// passthrough
|
||||
}
|
||||
} else {
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
return
|
||||
} else {
|
||||
// passthrough
|
||||
}
|
||||
}
|
||||
|
||||
token, err := verifyCommon(ctx, w, tokenStr)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
@@ -179,6 +201,24 @@ type VerifyTokenV3Param struct {
|
||||
// 200: tokens_AuthenticateV3Output
|
||||
func verifyTokensV3(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
tokenStr := r.Header.Get(api.AUTH_SUBJECT_TOKEN_HEADER)
|
||||
|
||||
valid, err := models.TokenCacheManager.IsValid(tokenStr)
|
||||
if err == nil {
|
||||
if !valid {
|
||||
httperrors.InvalidCredentialError(ctx, w, "invalid token")
|
||||
return
|
||||
} else {
|
||||
// passthrough
|
||||
}
|
||||
} else {
|
||||
if errors.Cause(err) != sql.ErrNoRows {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
return
|
||||
} else {
|
||||
// passthrough
|
||||
}
|
||||
}
|
||||
|
||||
token, err := verifyCommon(ctx, w, tokenStr)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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 tokens
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/appsrv"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
)
|
||||
|
||||
// swagger:route DELETE /v3/auth/tokens authentication invalidateTokensV3
|
||||
//
|
||||
// keystone v3删除token API
|
||||
//
|
||||
// keystone v3删除token API
|
||||
//
|
||||
func invalidateTokenV3(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
tokenStr := r.Header.Get(api.AUTH_SUBJECT_TOKEN_HEADER)
|
||||
err := invalidateToken(ctx, tokenStr)
|
||||
if err != nil {
|
||||
httperrors.GeneralServerError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
appsrv.SendNoContent(w)
|
||||
}
|
||||
|
||||
func invalidateToken(ctx context.Context, tokenStr string) error {
|
||||
adminToken := policy.FetchUserCredential(ctx)
|
||||
if adminToken == nil || len(tokenStr) == 0 {
|
||||
return httperrors.NewForbiddenError("missing auth token")
|
||||
}
|
||||
if adminToken.IsAllow(rbacscope.ScopeSystem, api.SERVICE_TYPE, "tokens", "delete").Result.IsDeny() {
|
||||
return httperrors.NewForbiddenError("%s not allow to auth", adminToken.GetUserName())
|
||||
}
|
||||
token := SAuthToken{}
|
||||
err := token.ParseFernetToken(tokenStr)
|
||||
if err != nil {
|
||||
return httperrors.NewInvalidCredentialError(errors.Wrapf(err, "invalid token").Error())
|
||||
}
|
||||
err = models.TokenCacheManager.Invalidate(ctx, tokenStr, token.ExpiresAt, token.Method, token.AuditIds)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Insert")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// swagger:route GET /v3/auth/tokens/invalid authentication fetchInvalidTokensV3
|
||||
//
|
||||
// keystone v3获取被删除的token的列表API
|
||||
//
|
||||
// keystone v3获取被删除的token的列表API
|
||||
//
|
||||
func fetchInvalidTokensV3(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
}
|
||||
|
||||
func fetchInvalidTokens(ctx context.Context) ([]string, error) {
|
||||
adminToken := policy.FetchUserCredential(ctx)
|
||||
if adminToken == nil {
|
||||
return nil, httperrors.NewForbiddenError("missing auth token")
|
||||
}
|
||||
if adminToken.IsAllow(rbacscope.ScopeSystem, api.SERVICE_TYPE, "tokens", "list", "invalid").Result.IsDeny() {
|
||||
return nil, httperrors.NewForbiddenError("%s not allow to list invalid tokens", adminToken.GetUserName())
|
||||
}
|
||||
tokens, err := models.TokenCacheManager.FetchInvalidTokens()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "TokenCacheManager.FetchInvalidTokens")
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
@@ -21,9 +21,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-plus/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vmihailenco/msgpack"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/netutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/syncman"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
@@ -140,6 +141,17 @@ func (c *TokenCacheVerify) Verify(ctx context.Context, cli *mcclient.Client, adm
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
func (c *TokenCacheVerify) Remove(ctx context.Context, cli *mcclient.Client, adminToken, token string) error {
|
||||
c.DeleteToken(token)
|
||||
|
||||
err := cli.Invalidate(ctx, adminToken, token)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Invalidate")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type authManager struct {
|
||||
syncman.SSyncManager
|
||||
|
||||
@@ -158,9 +170,35 @@ func newAuthManager(cli *mcclient.Client, info *AuthInfo) *authManager {
|
||||
accessKeyCache: newAccessKeyCache(),
|
||||
}
|
||||
authm.InitSync(authm)
|
||||
authm.startRefreshRevokeTokens()
|
||||
return authm
|
||||
}
|
||||
|
||||
func (a *authManager) startRefreshRevokeTokens() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
for range ticker.C {
|
||||
err := a.refreshRevokeTokens(context.Background())
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
ticker.Stop()
|
||||
}
|
||||
|
||||
func (a *authManager) refreshRevokeTokens(ctx context.Context) error {
|
||||
if a.adminCredential == nil {
|
||||
return fmt.Errorf("refreshRevokeTokens: No valid admin token credential")
|
||||
}
|
||||
tokens, err := a.client.FetchInvalidTokens(ctx, a.adminCredential.GetTokenString())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "client.FetchInvalidTokens")
|
||||
}
|
||||
for _, token := range tokens {
|
||||
a.tokenCacheVerify.DeleteToken(token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authManager) verifyRequest(req http.Request, virtualHost bool) (mcclient.TokenCredential, error) {
|
||||
if a.adminCredential == nil {
|
||||
return nil, fmt.Errorf("No valid admin token credential")
|
||||
@@ -174,15 +212,26 @@ func (a *authManager) verifyRequest(req http.Request, virtualHost bool) (mcclien
|
||||
|
||||
func (a *authManager) verify(ctx context.Context, token string) (mcclient.TokenCredential, error) {
|
||||
if a.adminCredential == nil {
|
||||
return nil, fmt.Errorf("No valid admin token credential")
|
||||
return nil, errors.Wrap(httperrors.ErrInvalidCredential, "No valid admin token credential")
|
||||
}
|
||||
cred, err := a.tokenCacheVerify.Verify(ctx, a.client, a.adminCredential.GetTokenString(), token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "tokenCacheVerify.Verify")
|
||||
}
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
func (a *authManager) remove(ctx context.Context, token string) error {
|
||||
if a.adminCredential == nil {
|
||||
return errors.Wrap(httperrors.ErrInvalidCredential, "No valid admin token credential")
|
||||
}
|
||||
err := a.tokenCacheVerify.Remove(ctx, a.client, a.adminCredential.GetTokenString(), token)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tokenCacheVerify.Remove")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
defaultAuthSource = mcclient.AuthSourceSrv
|
||||
)
|
||||
@@ -312,6 +361,10 @@ func Verify(ctx context.Context, tokenId string) (mcclient.TokenCredential, erro
|
||||
return manager.verify(ctx, tokenId)
|
||||
}
|
||||
|
||||
func Remove(ctx context.Context, tokenId string) error {
|
||||
return manager.remove(ctx, tokenId)
|
||||
}
|
||||
|
||||
func VerifyRequest(req http.Request, virtualHost bool) (mcclient.TokenCredential, error) {
|
||||
return manager.verifyRequest(req, virtualHost)
|
||||
}
|
||||
|
||||
+107
-81
@@ -91,41 +91,41 @@ func NewClient(authUrl string, timeout int, debug bool, insecure bool, certFile,
|
||||
return &client
|
||||
}
|
||||
|
||||
func (this *Client) HttpClient() *http.Client {
|
||||
return this.httpconn
|
||||
func (client *Client) HttpClient() *http.Client {
|
||||
return client.httpconn
|
||||
}
|
||||
|
||||
func (this *Client) SetHttpTransportProxyFunc(proxyFunc httputils.TransportProxyFunc) {
|
||||
httputils.SetClientProxyFunc(this.httpconn, proxyFunc)
|
||||
func (client *Client) SetHttpTransportProxyFunc(proxyFunc httputils.TransportProxyFunc) {
|
||||
httputils.SetClientProxyFunc(client.httpconn, proxyFunc)
|
||||
}
|
||||
|
||||
func (this *Client) GetClient() *http.Client {
|
||||
return this.httpconn
|
||||
func (client *Client) GetClient() *http.Client {
|
||||
return client.httpconn
|
||||
}
|
||||
|
||||
func (this *Client) SetTransport(ts http.RoundTripper) {
|
||||
this.httpconn.Transport = ts
|
||||
func (client *Client) SetTransport(ts http.RoundTripper) {
|
||||
client.httpconn.Transport = ts
|
||||
}
|
||||
|
||||
func (this *Client) SetDebug(debug bool) {
|
||||
this.debug = debug
|
||||
func (client *Client) SetDebug(debug bool) {
|
||||
client.debug = debug
|
||||
}
|
||||
|
||||
func (this *Client) GetDebug() bool {
|
||||
return this.debug
|
||||
func (client *Client) GetDebug() bool {
|
||||
return client.debug
|
||||
}
|
||||
|
||||
func (this *Client) AuthVersion() string {
|
||||
pos := strings.LastIndexByte(this.authUrl, '/')
|
||||
func (client *Client) AuthVersion() string {
|
||||
pos := strings.LastIndexByte(client.authUrl, '/')
|
||||
if pos > 0 {
|
||||
return this.authUrl[pos+1:]
|
||||
return client.authUrl[pos+1:]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Client) NewAuthTokenCredential() TokenCredential {
|
||||
if this.AuthVersion() == "v3" {
|
||||
func (client *Client) NewAuthTokenCredential() TokenCredential {
|
||||
if client.AuthVersion() == "v3" {
|
||||
return &TokenCredentialV3{}
|
||||
}
|
||||
return &TokenCredentialV2{}
|
||||
@@ -153,15 +153,15 @@ func joinUrl(baseUrl, path string) string {
|
||||
return fmt.Sprintf("%s%s", baseUrl, path)
|
||||
}
|
||||
|
||||
func (this *Client) rawRequest(ctx context.Context, endpoint string, token string, method httputils.THttpMethod, url string, header http.Header, body io.Reader) (*http.Response, error) {
|
||||
return httputils.Request(this.httpconn, ctx, method, joinUrl(endpoint, url), getDefaultHeader(header, token), body, this.debug)
|
||||
func (client *Client) rawRequest(ctx context.Context, endpoint string, token string, method httputils.THttpMethod, url string, header http.Header, body io.Reader) (*http.Response, error) {
|
||||
return httputils.Request(client.httpconn, ctx, method, joinUrl(endpoint, url), getDefaultHeader(header, token), body, client.debug)
|
||||
}
|
||||
|
||||
func (this *Client) jsonRequest(ctx context.Context, endpoint string, token string, method httputils.THttpMethod, url string, header http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
return httputils.JSONRequest(this.httpconn, ctx, method, joinUrl(endpoint, url), getDefaultHeader(header, token), body, this.debug)
|
||||
func (client *Client) jsonRequest(ctx context.Context, endpoint string, token string, method httputils.THttpMethod, url string, header http.Header, body jsonutils.JSONObject) (http.Header, jsonutils.JSONObject, error) {
|
||||
return httputils.JSONRequest(client.httpconn, ctx, method, joinUrl(endpoint, url), getDefaultHeader(header, token), body, client.debug)
|
||||
}
|
||||
|
||||
func (this *Client) _authV3(domainName, uname, passwd, projectId, projectName, projectDomain, token string, aCtx SAuthContext) (TokenCredential, error) {
|
||||
func (client *Client) _authV3(domainName, uname, passwd, projectId, projectName, projectDomain, token string, aCtx SAuthContext) (TokenCredential, error) {
|
||||
input := SAuthenticationInputV3{}
|
||||
if len(uname) > 0 && len(passwd) > 0 { // Password authentication
|
||||
input.Auth.Identity.Methods = []string{api.AUTH_METHOD_PASSWORD}
|
||||
@@ -190,24 +190,24 @@ func (this *Client) _authV3(domainName, uname, passwd, projectId, projectName, p
|
||||
// }
|
||||
}
|
||||
input.Auth.Context = aCtx
|
||||
return this._authV3Input(input)
|
||||
return client._authV3Input(input)
|
||||
}
|
||||
|
||||
func (this *Client) _authV3Input(input SAuthenticationInputV3) (TokenCredential, error) {
|
||||
hdr, rbody, err := this.jsonRequest(context.Background(), this.authUrl, "", "POST", "/auth/tokens", nil, jsonutils.Marshal(&input))
|
||||
func (client *Client) _authV3Input(input SAuthenticationInputV3) (TokenCredential, error) {
|
||||
hdr, rbody, err := client.jsonRequest(context.Background(), client.authUrl, "", "POST", "/auth/tokens", nil, jsonutils.Marshal(&input))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenId := hdr.Get("X-Subject-Token")
|
||||
if len(tokenId) == 0 {
|
||||
return nil, fmt.Errorf("No X-Subject-Token in header")
|
||||
return nil, errors.Wrap(httperrors.ErrInputParameter, "No X-Subject-Token in header")
|
||||
}
|
||||
ret, err := this.unmarshalV3Token(rbody, tokenId)
|
||||
ret, err := client.unmarshalV3Token(rbody, tokenId)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (this *Client) _authV2(uname, passwd, tenantId, tenantName, token string, aCtx SAuthContext) (TokenCredential, error) {
|
||||
func (client *Client) _authV2(uname, passwd, tenantId, tenantName, token string, aCtx SAuthContext) (TokenCredential, error) {
|
||||
input := SAuthenticationInputV2{}
|
||||
input.Auth.PasswordCredentials.Username = uname
|
||||
input.Auth.PasswordCredentials.Password = passwd
|
||||
@@ -221,144 +221,170 @@ func (this *Client) _authV2(uname, passwd, tenantId, tenantName, token string, a
|
||||
input.Auth.Token.Id = token
|
||||
}
|
||||
input.Auth.Context = aCtx
|
||||
_, rbody, err := this.jsonRequest(context.Background(), this.authUrl, "", "POST", "/tokens", nil, jsonutils.Marshal(&input))
|
||||
_, rbody, err := client.jsonRequest(context.Background(), client.authUrl, "", "POST", "/tokens", nil, jsonutils.Marshal(&input))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.unmarshalV2Token(rbody)
|
||||
return client.unmarshalV2Token(rbody)
|
||||
}
|
||||
|
||||
func (this *Client) Authenticate(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) {
|
||||
return this.AuthenticateApi(uname, passwd, domainName, tenantName, tenantDomain)
|
||||
func (client *Client) Authenticate(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) {
|
||||
return client.AuthenticateApi(uname, passwd, domainName, tenantName, tenantDomain)
|
||||
}
|
||||
|
||||
func (this *Client) AuthenticateApi(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) {
|
||||
return this.AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain, AuthSourceAPI)
|
||||
func (client *Client) AuthenticateApi(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) {
|
||||
return client.AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain, AuthSourceAPI)
|
||||
}
|
||||
|
||||
func (this *Client) AuthenticateWeb(uname, passwd, domainName, tenantName, tenantDomain string, cliIp string) (TokenCredential, error) {
|
||||
func (client *Client) AuthenticateWeb(uname, passwd, domainName, tenantName, tenantDomain string, cliIp string) (TokenCredential, error) {
|
||||
aCtx := SAuthContext{
|
||||
Source: AuthSourceWeb,
|
||||
Ip: cliIp,
|
||||
}
|
||||
return this.authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain, aCtx)
|
||||
return client.authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain, aCtx)
|
||||
}
|
||||
|
||||
func (this *Client) AuthenticateOperator(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) {
|
||||
return this.AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain, AuthSourceOperator)
|
||||
func (client *Client) AuthenticateOperator(uname, passwd, domainName, tenantName, tenantDomain string) (TokenCredential, error) {
|
||||
return client.AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain, AuthSourceOperator)
|
||||
}
|
||||
|
||||
func (this *Client) AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain string, source string) (TokenCredential, error) {
|
||||
func (client *Client) AuthenticateWithSource(uname, passwd, domainName, tenantName, tenantDomain string, source string) (TokenCredential, error) {
|
||||
aCtx := SAuthContext{
|
||||
Source: source,
|
||||
}
|
||||
return this.authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain, aCtx)
|
||||
return client.authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain, aCtx)
|
||||
}
|
||||
|
||||
func (this *Client) authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain string, aCtx SAuthContext) (TokenCredential, error) {
|
||||
if this.AuthVersion() == "v3" {
|
||||
return this._authV3(domainName, uname, passwd, "", tenantName, tenantDomain, "", aCtx)
|
||||
func (client *Client) authenticateWithContext(uname, passwd, domainName, tenantName, tenantDomain string, aCtx SAuthContext) (TokenCredential, error) {
|
||||
if client.AuthVersion() == "v3" {
|
||||
return client._authV3(domainName, uname, passwd, "", tenantName, tenantDomain, "", aCtx)
|
||||
}
|
||||
return this._authV2(uname, passwd, "", tenantName, "", aCtx)
|
||||
return client._authV2(uname, passwd, "", tenantName, "", aCtx)
|
||||
}
|
||||
|
||||
func (this *Client) unmarshalV3Token(rbody jsonutils.JSONObject, tokenId string) (cred TokenCredential, err error) {
|
||||
func (client *Client) unmarshalV3Token(rbody jsonutils.JSONObject, tokenId string) (cred TokenCredential, err error) {
|
||||
cred = &TokenCredentialV3{Id: tokenId}
|
||||
err = rbody.Unmarshal(cred)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Invalid response when unmarshal V3 Token: %v", err)
|
||||
err = errors.Wrap(err, "Invalid response when unmarshal V3 Token")
|
||||
}
|
||||
cata := cred.GetServiceCatalog()
|
||||
if cata == nil || cata.Len() == 0 {
|
||||
log.Warningf("No service catalog avaiable")
|
||||
} else {
|
||||
this.SetServiceCatalog(cata)
|
||||
client.SetServiceCatalog(cata)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Client) unmarshalV2Token(rbody jsonutils.JSONObject) (cred TokenCredential, err error) {
|
||||
func (client *Client) unmarshalV2Token(rbody jsonutils.JSONObject) (cred TokenCredential, err error) {
|
||||
access, err := rbody.Get("access")
|
||||
if err == nil {
|
||||
cred = &TokenCredentialV2{}
|
||||
err = access.Unmarshal(cred)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Invalid response when unmarshal V2 Token: %s", err)
|
||||
err = errors.Wrap(err, "Invalid response when unmarshal V2 Token")
|
||||
}
|
||||
cata := cred.GetServiceCatalog()
|
||||
if cata == nil || cata.Len() == 0 {
|
||||
log.Warningf("No srvice catalog avaiable")
|
||||
} else {
|
||||
this.SetServiceCatalog(cata)
|
||||
client.SetServiceCatalog(cata)
|
||||
}
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("Invalid response: no access object")
|
||||
err = errors.Wrap(httperrors.ErrInvalidFormat, "Invalid response: no access object")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Client) verifyV3(adminToken, token string) (TokenCredential, error) {
|
||||
func (client *Client) verifyV3(adminToken, token string) (TokenCredential, error) {
|
||||
header := http.Header{}
|
||||
header.Add(api.AUTH_TOKEN_HEADER, adminToken)
|
||||
header.Add(api.AUTH_SUBJECT_TOKEN_HEADER, token)
|
||||
_, rbody, err := this.jsonRequest(context.Background(), this.authUrl, "", "GET", "/auth/tokens", header, nil)
|
||||
_, rbody, err := client.jsonRequest(context.Background(), client.authUrl, "", "GET", "/auth/tokens", header, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.unmarshalV3Token(rbody, token)
|
||||
return client.unmarshalV3Token(rbody, token)
|
||||
}
|
||||
|
||||
func (this *Client) verifyV2(adminToken, token string) (TokenCredential, error) {
|
||||
func (client *Client) verifyV2(adminToken, token string) (TokenCredential, error) {
|
||||
header := http.Header{}
|
||||
header.Add(api.AUTH_TOKEN_HEADER, adminToken)
|
||||
verifyUrl := fmt.Sprintf("/tokens/%s", token)
|
||||
_, rbody, err := this.jsonRequest(context.Background(), this.authUrl, "", "GET", verifyUrl, header, nil)
|
||||
_, rbody, err := client.jsonRequest(context.Background(), client.authUrl, "", "GET", verifyUrl, header, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return this.unmarshalV2Token(rbody)
|
||||
return client.unmarshalV2Token(rbody)
|
||||
}
|
||||
|
||||
func (this *Client) Verify(adminToken, token string) (cred TokenCredential, err error) {
|
||||
if this.AuthVersion() == "v3" {
|
||||
return this.verifyV3(adminToken, token)
|
||||
func (client *Client) Verify(adminToken, token string) (cred TokenCredential, err error) {
|
||||
if client.AuthVersion() == "v3" {
|
||||
return client.verifyV3(adminToken, token)
|
||||
}
|
||||
return this.verifyV2(adminToken, token)
|
||||
return client.verifyV2(adminToken, token)
|
||||
}
|
||||
|
||||
func (this *Client) SetTenant(tenantId, tenantName, tenantDomain string, token TokenCredential) (TokenCredential, error) {
|
||||
return this.SetProject(tenantId, tenantName, tenantDomain, token)
|
||||
func (client *Client) Invalidate(ctx context.Context, adminToken, token string) error {
|
||||
header := http.Header{}
|
||||
header.Add(api.AUTH_TOKEN_HEADER, adminToken)
|
||||
header.Add(api.AUTH_SUBJECT_TOKEN_HEADER, token)
|
||||
_, _, err := client.jsonRequest(ctx, client.authUrl, "", "DELETE", "/auth/tokens", header, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "jsonRequest")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Client) AuthenticateToken(token string, projName, projDomain string, source string) (TokenCredential, error) {
|
||||
func (client *Client) FetchInvalidTokens(ctx context.Context, adminToken string) ([]string, error) {
|
||||
header := http.Header{}
|
||||
header.Add(api.AUTH_TOKEN_HEADER, adminToken)
|
||||
_, resp, err := client.jsonRequest(ctx, client.authUrl, "", "GET", "/auth/tokens/invalid", header, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "jsonRequest")
|
||||
}
|
||||
tokens := make([]string, 0)
|
||||
err = resp.Unmarshal(&tokens, "tokens")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Unmarshal")
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (client *Client) SetTenant(tenantId, tenantName, tenantDomain string, token TokenCredential) (TokenCredential, error) {
|
||||
return client.SetProject(tenantId, tenantName, tenantDomain, token)
|
||||
}
|
||||
|
||||
func (client *Client) AuthenticateToken(token string, projName, projDomain string, source string) (TokenCredential, error) {
|
||||
aCtx := SAuthContext{
|
||||
Source: source,
|
||||
}
|
||||
if this.AuthVersion() == "v3" {
|
||||
return this._authV3("", "", "", "", projName, projDomain, token, aCtx)
|
||||
if client.AuthVersion() == "v3" {
|
||||
return client._authV3("", "", "", "", projName, projDomain, token, aCtx)
|
||||
} else {
|
||||
return this._authV2("", "", "", projName, token, aCtx)
|
||||
return client._authV2("", "", "", projName, token, aCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Client) SetProject(tenantId, tenantName, tenantDomain string, token TokenCredential) (TokenCredential, error) {
|
||||
func (client *Client) SetProject(tenantId, tenantName, tenantDomain string, token TokenCredential) (TokenCredential, error) {
|
||||
aCtx := SAuthContext{
|
||||
Source: token.GetLoginSource(),
|
||||
Ip: token.GetLoginIp(),
|
||||
}
|
||||
if this.AuthVersion() == "v3" {
|
||||
return this._authV3("", "", "", tenantId, tenantName, tenantDomain, token.GetTokenString(), aCtx)
|
||||
if client.AuthVersion() == "v3" {
|
||||
return client._authV3("", "", "", tenantId, tenantName, tenantDomain, token.GetTokenString(), aCtx)
|
||||
} else {
|
||||
return this._authV2("", "", "", tenantName, token.GetTokenString(), aCtx)
|
||||
return client._authV2("", "", "", tenantName, token.GetTokenString(), aCtx)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Client) GetCommonEtcdEndpoint(token TokenCredential, region, interfaceType string) (*api.EndpointDetails, error) {
|
||||
if this.AuthVersion() != "v3" {
|
||||
return nil, errors.Errorf("current version %s not support get internal etcd endpoint", this.AuthVersion())
|
||||
func (client *Client) GetCommonEtcdEndpoint(token TokenCredential, region, interfaceType string) (*api.EndpointDetails, error) {
|
||||
if client.AuthVersion() != "v3" {
|
||||
return nil, errors.Errorf("current version %s not support get internal etcd endpoint", client.AuthVersion())
|
||||
}
|
||||
|
||||
_, err := this.GetServiceCatalog().getServiceURL(apis.SERVICE_TYPE_ETCD, region, "", interfaceType)
|
||||
_, err := client.GetServiceCatalog().getServiceURL(apis.SERVICE_TYPE_ETCD, region, "", interfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -371,7 +397,7 @@ func (this *Client) GetCommonEtcdEndpoint(token TokenCredential, region, interfa
|
||||
params.Add(jsonutils.NewString(region), "region")
|
||||
|
||||
epUrl := "/endpoints?" + params.QueryString()
|
||||
_, rbody, err := this.jsonRequest(context.Background(), this.authUrl, token.GetTokenString(), httputils.GET, epUrl, nil, nil)
|
||||
_, rbody, err := client.jsonRequest(context.Background(), client.authUrl, token.GetTokenString(), httputils.GET, epUrl, nil, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get internal etcd endpoint")
|
||||
}
|
||||
@@ -392,7 +418,7 @@ func (this *Client) GetCommonEtcdEndpoint(token TokenCredential, region, interfa
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
func (this *Client) GetCommonEtcdTLSConfig(endpoint *api.EndpointDetails) (*tls.Config, error) {
|
||||
func (client *Client) GetCommonEtcdTLSConfig(endpoint *api.EndpointDetails) (*tls.Config, error) {
|
||||
if endpoint.CertId == "" {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -402,13 +428,13 @@ func (this *Client) GetCommonEtcdTLSConfig(endpoint *api.EndpointDetails) (*tls.
|
||||
return seclib2.InitTLSConfigByData(caData, certData, keyData)
|
||||
}
|
||||
|
||||
func (this *Client) NewSession(ctx context.Context, region, zone, endpointType string, token TokenCredential) *ClientSession {
|
||||
func (client *Client) NewSession(ctx context.Context, region, zone, endpointType string, token TokenCredential) *ClientSession {
|
||||
cata := token.GetServiceCatalog()
|
||||
if this.GetServiceCatalog() == nil {
|
||||
if client.GetServiceCatalog() == nil {
|
||||
if cata == nil || cata.Len() == 0 {
|
||||
log.Warningf("Missing service catalog in token")
|
||||
} else {
|
||||
this.SetServiceCatalog(cata)
|
||||
client.SetServiceCatalog(cata)
|
||||
}
|
||||
}
|
||||
if ctx == nil {
|
||||
@@ -416,7 +442,7 @@ func (this *Client) NewSession(ctx context.Context, region, zone, endpointType s
|
||||
}
|
||||
return &ClientSession{
|
||||
ctx: ctx,
|
||||
client: this,
|
||||
client: client,
|
||||
region: region,
|
||||
zone: zone,
|
||||
endpointType: endpointType,
|
||||
|
||||
Reference in New Issue
Block a user