add keystone middleware for token validation
Change-Id: If3478342eaafbb61e6f99841d4930b9858dd23ac
This commit is contained in:
parent
16ac1f84b2
commit
657e32bbd4
46
identity/middleware/types.go
Normal file
46
identity/middleware/types.go
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2016 eBay Inc.
|
||||
//
|
||||
// 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 middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.openstack.org/openstack/golang-client.git/openstack"
|
||||
)
|
||||
|
||||
type Validator struct {
|
||||
// Service account to talk to keystone
|
||||
SvcAuthOpts openstack.AuthOpts
|
||||
// File path the signing cert would be stored/cached
|
||||
CachedSigningKeyPath string
|
||||
TokenId string
|
||||
// Token revocation list memory cache duration
|
||||
RevCacheDuration time.Duration
|
||||
}
|
||||
|
||||
// Token revocation response structure
|
||||
type revokeResp struct {
|
||||
Signed string `json:"signed"`
|
||||
}
|
||||
|
||||
type revokedListCache struct {
|
||||
Revoked []openstack.Token
|
||||
// time when this cache was built
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
type RevokedList struct {
|
||||
Revoked []openstack.Token
|
||||
}
|
345
identity/middleware/validation.go
Normal file
345
identity/middleware/validation.go
Normal file
@ -0,0 +1,345 @@
|
||||
// Copyright (c) 2016 eBay Inc.
|
||||
//
|
||||
// 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 middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/md5"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.openstack.org/openstack/golang-client.git/openstack"
|
||||
"github.com/fullsailor/pkcs7"
|
||||
)
|
||||
|
||||
const (
|
||||
PKI_ASN1_PREFIX = "MII"
|
||||
PKIZ_PREFIX = "PKIZ_"
|
||||
)
|
||||
|
||||
// Cache the token until it gets expired
|
||||
var serviceTokenSession *openstack.Session
|
||||
|
||||
// Cache token revocation list until expired
|
||||
var revocationListCache *revokedListCache
|
||||
|
||||
// NewValidator gets the credential for service account, token need to be validated,
|
||||
// signing cert location (will store the cert from keystone if not there), and the
|
||||
// revocation list cache duration (in seconds) and returns the validator.
|
||||
func NewValidator(authOpts openstack.AuthOpts, token string, signingKeyPath string, revCacheSecs int) *Validator {
|
||||
return &Validator{
|
||||
SvcAuthOpts: authOpts,
|
||||
CachedSigningKeyPath: signingKeyPath,
|
||||
TokenId: token,
|
||||
RevCacheDuration: time.Duration(revCacheSecs) * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate does the local validation for PKI & PKIZ token and sends to keystone
|
||||
// for other format tokens validation. It returns the extracted AuthToken struct
|
||||
func (validator *Validator) Validate() (*openstack.AuthToken, error) {
|
||||
var token *openstack.AuthToken
|
||||
|
||||
if strings.HasPrefix(validator.TokenId, PKIZ_PREFIX) ||
|
||||
strings.HasPrefix(validator.TokenId, PKI_ASN1_PREFIX) {
|
||||
// do local validation for PKI and PKIZ token
|
||||
if revocationListCache == nil || time.Now().Sub(revocationListCache.Time) > validator.RevCacheDuration {
|
||||
_, err := validator.getRevocationList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
access, err := validator.ValidateOffline()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(access, &token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set the token ID
|
||||
token.Access.Token.ID = validator.TokenId
|
||||
|
||||
hashedTokenId := fmt.Sprintf("%x", md5.Sum([]byte(validator.TokenId)))
|
||||
for _, rtoken := range revocationListCache.Revoked {
|
||||
if rtoken.ID == hashedTokenId {
|
||||
return nil, fmt.Errorf("token %s was revoked", hashedTokenId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
access, err := validator.ValidateRemote()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(access, &token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// validation should fail if token is expired
|
||||
if token.Access.Token.Expires.Sub(time.Now()) < 0 {
|
||||
return nil, fmt.Errorf("token %s is expired", validator.TokenId)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Validate the token locally without sending to keystone
|
||||
// It take the token body and return the extracted access object as []byte
|
||||
func (validator *Validator) ValidateOffline() ([]byte, error) {
|
||||
token := ""
|
||||
switch {
|
||||
case strings.HasPrefix(validator.TokenId, PKIZ_PREFIX):
|
||||
token = strings.TrimPrefix(validator.TokenId, PKIZ_PREFIX)
|
||||
decompressedToken, err := decompressToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token = trimCMSFormat(decompressedToken)
|
||||
case strings.HasPrefix(validator.TokenId, PKI_ASN1_PREFIX):
|
||||
token = validator.TokenId
|
||||
|
||||
default:
|
||||
return nil, errors.New("can not validate offline, it has to be sent to keystone")
|
||||
}
|
||||
|
||||
decodedToken, err := base64DecodeFromCms(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := validator.checkSignature(decodedToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (validator *Validator) ValidateRemote() ([]byte, error) {
|
||||
resp, err := validator.reqTokenValidation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusUnauthorized {
|
||||
// retry one more time using the new service token
|
||||
err = validator.renewToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err = validator.reqTokenValidation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("error reading response body")
|
||||
}
|
||||
return rbody, nil
|
||||
}
|
||||
|
||||
func decompressToken(token string) (string, error) {
|
||||
decToken, err := base64.URLEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
zr, err := zlib.NewReader(bytes.NewBuffer(decToken))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bb, err := ioutil.ReadAll(zr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bb), nil
|
||||
}
|
||||
|
||||
func base64DecodeFromCms(token string) ([]byte, error) {
|
||||
t := strings.Replace(token, "-", "/", -1)
|
||||
decToken, err := base64.StdEncoding.DecodeString(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decToken, nil
|
||||
}
|
||||
|
||||
// remove the customerized header and footer in PEM token
|
||||
// -----BEGIN CMS-----
|
||||
// -----END CMS-----
|
||||
func trimCMSFormat(token string) string {
|
||||
l := strings.Index(token, "\n")
|
||||
r := strings.LastIndex(token, "\n")
|
||||
t := token[l:r]
|
||||
r2 := strings.LastIndex(t, "\n")
|
||||
return t[0:r2]
|
||||
}
|
||||
|
||||
// Get the signging certificate from local dir
|
||||
// It will get the cert from keystone if cache file does not exist
|
||||
func (validator *Validator) getSigningCert() (*x509.Certificate, error) {
|
||||
signPEM, err := ioutil.ReadFile(validator.CachedSigningKeyPath)
|
||||
if err != nil {
|
||||
resp, err := http.Get(validator.SvcAuthOpts.AuthUrl + "/certificates/signing")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New("can not get signing cert")
|
||||
}
|
||||
signPEM, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// cache the file to location
|
||||
if err = ioutil.WriteFile(validator.CachedSigningKeyPath, []byte(signPEM), 0644); err != nil {
|
||||
log.Println("error caching signging cert")
|
||||
}
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(signPEM)
|
||||
if block == nil {
|
||||
return nil, errors.New("can not decode PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// check the signature of the token
|
||||
func (validator *Validator) checkSignature(data []byte) ([]byte, error) {
|
||||
p7, err := pkcs7.Parse(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(p7.Signers) != 1 {
|
||||
return nil, errors.New("should be only one signature found")
|
||||
}
|
||||
|
||||
signer := p7.Signers[0]
|
||||
cert, err := validator.getSigningCert()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = cert.CheckSignature(x509.SHA256WithRSA, p7.Content, signer.EncryptedDigest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p7.Content, nil
|
||||
}
|
||||
|
||||
// getRevocationList get a list of revoked tokens
|
||||
func (validator *Validator) getRevocationList() ([]openstack.Token, error) {
|
||||
// Get the service token to get the token revocation list
|
||||
resp, err := validator.reqRevocationList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusUnauthorized {
|
||||
// try again when getting 401, it could be the cached token was revoked
|
||||
// or the token got expired
|
||||
validator.renewToken()
|
||||
resp, err = validator.reqRevocationList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
revokeCMSResp := revokeResp{}
|
||||
if err = json.Unmarshal(rbody, &revokeCMSResp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
revokeResp := trimCMSFormat(revokeCMSResp.Signed)
|
||||
decodedResp, err := base64DecodeFromCms(revokeResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
revoked, err := validator.checkSignature(decodedResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revokedList := RevokedList{}
|
||||
if err = json.Unmarshal(revoked, &revokedList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update the revocation list cache
|
||||
revocationListCache = &revokedListCache{
|
||||
Revoked: revokedList.Revoked,
|
||||
Time: time.Now(),
|
||||
}
|
||||
return revokedList.Revoked, nil
|
||||
}
|
||||
|
||||
// reqRevocationList sends the GET request to keystone and get response back
|
||||
func (validator *Validator) reqRevocationList() (*http.Response, error) {
|
||||
if serviceTokenSession == nil {
|
||||
validator.renewToken()
|
||||
}
|
||||
// Get token revocation list
|
||||
return serviceTokenSession.Request("GET", validator.SvcAuthOpts.AuthUrl+"/tokens/revoked", nil, nil, nil)
|
||||
}
|
||||
|
||||
func (validator *Validator) reqTokenValidation() (*http.Response, error) {
|
||||
if serviceTokenSession == nil {
|
||||
validator.renewToken()
|
||||
}
|
||||
// Get token revocation list
|
||||
reqUrl := fmt.Sprintf("%s/tokens/%s", validator.SvcAuthOpts.AuthUrl, validator.TokenId)
|
||||
return serviceTokenSession.Request("GET", reqUrl, nil, nil, nil)
|
||||
}
|
||||
|
||||
// renewToken gets the keystone token from service AuthOpts
|
||||
func (validator *Validator) renewToken() error {
|
||||
// Get the service token to get the token revocation list
|
||||
auth, err := openstack.DoAuthRequest(validator.SvcAuthOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make a new client with these creds
|
||||
serviceTokenSession, err = openstack.NewSession(nil, auth, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
87
identity/middleware/validation_test.go
Normal file
87
identity/middleware/validation_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2016 eBay Inc.
|
||||
//
|
||||
// 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 middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.openstack.org/openstack/golang-client.git/openstack"
|
||||
)
|
||||
|
||||
const (
|
||||
cacheKeyPath = "/tmp/signing.PEM"
|
||||
)
|
||||
|
||||
func getAuthOpts() openstack.AuthOpts {
|
||||
return openstack.AuthOpts{
|
||||
AuthUrl: "http://localhost:5000/v2.0",
|
||||
Project: "demo",
|
||||
Username: "demo",
|
||||
Password: "demo",
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLocalPKIZ(t *testing.T) {
|
||||
token := `PKIZ_eJxdVduSqjoQfecrzvvUruEijj6GWwRNGBQC4U3AgXDTGVQuX78jzpy9z7EqVdJ091qru9P8-sV_mglt_I-ODo-HXwKybVMB1NmeY7u4pxh4qX7mtsnXdQ1VOvDMwfDBTsvrvKhyLfaQCXKzz3P0qeXVp1BUDK57UdN6mHNjotm2YSkdoyEu4whPdnnOT6MjJvK1Tpi9tDcdO3J7FpIqki1ReDjYpd0jn16REfCDA-RXS-yDJfLzG_bNHk25bLM-txuySGE9xhFiLgMMlWAQMBN7fODH8gbXP0vIOA-udWY7HbBj6M2OKRwKKpNzIg9VHNnzuwySGw3VJwNOsUyUZ9YYkm-0fZ3KeDxGmngM17eHX9KSLtFnJjzY6WIOILhME9OW1H8jIogGXCLV9R1GZVxjIxjiMhfxxOX4cY1CU0E-EF0jZ8JMp9XKuRYcNWN2Z-sOR5DqH1sSrtjH4YFsSTGsxUiuq3TkBbU6JiCzGo6lLWOlPtBgJXlS3PpV7RFLI9noZHNgi-u0jS-8DgaN9kUC1-0jeci7JcShWqVwfUna_fRjpOH-egxVElT9LOm42Yupcb7v5OFOZas7wvWUGecJ-0gS8LiWEVv0c-Fap47l-j73W3emDFptfJgl9Rkv2jHE_yZ9FrquvucAVxjihvrBSENPdn1voBNhbkhqPKUqLrMKTUhxDYvZrZQ96vbDXHhQj-ThkjTXpwRJfCCKp0j7LmJdcWfxGO3VJ6paJGHwlCYTVUiV_T1pAvZB5kAplcn4PRvz_6SxrlzGQxJPJN2_pd4TSGZg4X_Il1h_ONsTLp2Kyp6MIZVjvxJx6Sm01Gpc1txuL1wDl3SyJ8HV5yKNfEzrZ2-vb3_T9ENypQ1n1XIAfpH-YlTEUOIMmLOODv_Vxsf5mozO-lGsJCQib2uRQZO_03jymlO3qiSs5_EWkP5H0yxltNW4Cca4oWocBiK_iSItzYXr7wvXBws8FRWSAxU1TkH51AqcSoNCXGCjYLGRirFh87OvKQ_AvtfTho7I8HgSr0c6n8xIWiNqawVfOICausA3Th8ZWoS8rtc9ahDPg2bvkGAyXQR6CKTA1EFvkVCd4sgbjBIgLcdEAynSCLkJvFUi8ha9AebgrQEISfjsZ4ZJkUbnBKDv3zMlU3Z8ofBL1icwuFF5feWrTRMQ6KAe6vAArBr0ng58gB-77mfVcYYWAAe-G_WCnrqRmp_Wockkpwx39CV0BZvhXLZ2b2R4lWm1ircIOjp6Z8Dp9Oh4wtsV7PL0vicvm3fn86gV7eErk3R7a42bdukHggOOh6F6Wb1btCPZdoViT_psEaSAeY6rh4ssjr6KF602S_08sZ7mUA6i5uYaBdRongkrkOpKkOUsqqRop1b5eXz9APS1BRejWYr4a2dU4qbQGnyzbu5YFiT3P5rUNUv1w98ub4L0vowcxdJEiymnfLOXqvtp97rb7_F60jbs6y2kfgmvqvylti8Ef8rvO3OZv3pfLBET1r0IoyeudBj24IK2NLSNKhmUj9gRr_gmS6feGRdLaVGSLdhWa2Le3tCFdxePi1twmNz3ggnz98nExp9v1W82DTmf`
|
||||
validator := NewValidator(getAuthOpts(), token, cacheKeyPath, 6)
|
||||
access, err := validator.Validate()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(access)
|
||||
project := access.Access.Token.Project
|
||||
if project.Name != "demo" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLocalPKI(t *testing.T) {
|
||||
token := `MIIE3AYJKoZIhvcNAQcCoIIEzTCCBMkCAQExDTALBglghkgBZQMEAgEwggMqBgkqhkiG9w0BBwGgggMbBIIDF3siYWNjZXNzIjogeyJ0b2tlbiI6IHsiaXNzdWVkX2F0IjogIjIwMTYtMDUtMDNUMTk6NTE6MTIuNTM1NzQzIiwgImV4cGlyZXMiOiAiMjAxNi0wNS0wNFQxOTo1MToxMloiLCAiaWQiOiAicGxhY2Vob2xkZXIiLCAidGVuYW50IjogeyJjb3MiOiAiZGV2IiwgImRlc2NyaXB0aW9uIjogbnVsbCwgImVuYWJsZWQiOiB0cnVlLCAiaWQiOiAiMGMxNjM5OTJiY2NlNDUxZjg0NzEwMTZlMWE3MTA0ODgiLCAidnBjIjogImRldiIsICJuYW1lIjogImRlbW8ifSwgImF1ZGl0X2lkcyI6IFsiS01WV3F1U3NSYkdaTEc1Q0E2YnE2ZyJdfSwgInNlcnZpY2VDYXRhbG9nIjogW3siZW5kcG9pbnRzIjogW3siYWRtaW5VUkwiOiAiaHR0cDovL2xvY2FsaG9zdDozNTM1Ny92Mi4wIiwgInJlZ2lvbiI6ICJzdGFnZSIsICJwdWJsaWNVUkwiOiAiIiwgImlkIjogIjNkNGNmYTUyYWQ2OTQxYzViOWVlNzc5NjdkMzM3ODFiIn1dLCAiZW5kcG9pbnRzX2xpbmtzIjogW10sICJ0eXBlIjogImlkZW50aXR5IiwgIm5hbWUiOiAia2V5c3RvbmUifV0sICJ1c2VyIjogeyJ1c2VybmFtZSI6ICJkZW1vIiwgInJvbGVzX2xpbmtzIjogW10sICJpZCI6ICIzNjJkY2Q2NGY2ZTk0NjQ3YjBlNjlkY2I4ODNjYzIzOCIsICJyb2xlcyI6IFt7Im5hbWUiOiAiTWVtYmVyIn0sIHsibmFtZSI6ICJhZG1pbiJ9XSwgIm5hbWUiOiAiZGVtbyJ9LCAibWV0YWRhdGEiOiB7ImlzX2FkbWluIjogMCwgInJvbGVzIjogWyI5ZmUyZmY5ZWU0Mzg0YjE4OTRhOTA4NzhkM2U5MmJhYiIsICJmMWNhNDhiZDc0ZDI0ZDRlYTRhNTQwYmYyMDQ0YjQwMCJdfX19MYIBhTCCAYECAQEwXDBXMQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVW5zZXQxDjAMBgNVBAcMBVVuc2V0MQ4wDAYDVQQKDAVVbnNldDEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29tAgEBMAsGCWCGSAFlAwQCATANBgkqhkiG9w0BAQEFAASCAQBKR9e1K9TYOanIHpoxpCwKjnFY1Ue66+GbKVr956TLA+d2Q82IS-vJpmGRdxZh0t05knoErJEwjaq2XtA2subfIPnX6zm34y6Q1f8AJXDUowWX8YeeyRs548oCdaHoE1ak81jGOzYjMhZc-kljUlEDE4ejlO4wkxCnagDiA7uaRJgmSzB2kuuKZeeMxhlTe78tkoco3a1gZCGjGsUuEzbH5HU6RSugI5uxUGyMW0PS2j4K2+BBq2Uk-nHX0pIb513NOoDZztVq6ZuYx3KPIe-h29IMzoqL9OcZ4JH49ehzlDlTw8otu8wS8JUaIv7HNnGgJbCbsUmQOPvWju89rB3k`
|
||||
validator := NewValidator(getAuthOpts(), token, cacheKeyPath, 6)
|
||||
access, err := validator.Validate()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(access)
|
||||
project := access.Access.Token.Project
|
||||
if project.Name != "demo" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyLocalUUID(t *testing.T) {
|
||||
token := `399789012f4fbedc63c55396f59654d6`
|
||||
validator := NewValidator(getAuthOpts(), token, cacheKeyPath, 120)
|
||||
access, err := validator.Validate()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(access)
|
||||
project := access.Access.Token.Project
|
||||
if project.Name != "demo" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// Should be half traffic sending to keystone, not every time
|
||||
func TestCache(t *testing.T) {
|
||||
for i := 0; i < 4; i++ {
|
||||
TestVerifyLocalPKIZ(t)
|
||||
TestVerifyLocalPKI(t)
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -78,7 +79,10 @@ func DoAuthRequest(authopts AuthOpts) (AuthRef, error) {
|
||||
|
||||
path := auth_mod.AuthUrl + "/tokens"
|
||||
body := auth_mod.JSON()
|
||||
resp, err := Post(path, nil, nil, &body)
|
||||
headers := &http.Header{}
|
||||
headers.Add("Content-Type", "application/json")
|
||||
|
||||
resp, err := Post(path, nil, headers, &body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user