Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ func (c *Client) AddEncryption(caFile string, keyFile string, certFile string, i
return nil
}
tlsConfig.Certificates = []tls.Certificate{cert}
} else {
tlsConfig.InsecureSkipVerify = true
}
}
transport := &http.Transport{
Expand Down Expand Up @@ -118,6 +120,7 @@ type Client struct {
port int
httpClient *http.Client
auth Authenticator
encrypt Encryptor

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit confused about this field, didn't find the usage of it. From my understanding, the encryption setting is done by function AddEncryption, which is a member of Client, is it right?

}

type TlsConfig struct {
Expand Down Expand Up @@ -146,6 +149,7 @@ func NewClient(server string, port int) *Client {
client.scheme = "http"
client.httpClient = &http.Client{}
client.auth = new(NopAuthenticator)
client.encrypt = new(NopEncryptor)
return client
}

Expand All @@ -160,6 +164,11 @@ func (c *Client) SetAuthenticator(auth Authenticator) {
c.auth = auth
}

// SetEncryptor enables the user to encrypt the API traffic
func (c *Client) SetEncryptor(encrypt Encryptor) {
c.encrypt = encrypt
}

func typename(ptr IObject) string {
name := reflect.TypeOf(ptr).Elem().Name()
var buf []rune
Expand Down
119 changes: 104 additions & 15 deletions keystone.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ type KeystoneClient struct {
osUsername string
osPassword string
osAdminToken string

current *KeystoneToken

httpClient *http.Client
current *KeystoneToken
httpClient *http.Client
tokenID string
isv3Client bool
issuedAt string
expiresAt string
}

// KeepaliveKeystoneClient embeds KeystoneClient
Expand All @@ -49,6 +51,13 @@ type KeystoneToken struct {
Issued_At string
}

type KeystoneTokenv3 struct {
Token struct {
ExpiresAt string `json:"expires_at"`
IssuedAt string `json:"issued_at"`
} `json:"token"`
}

// NewKeystoneClient allocates and initializes a KeystoneClient
func NewKeystoneClient(auth_url, tenant_name, username, password, token string) *KeystoneClient {
return &KeystoneClient{
Expand Down Expand Up @@ -77,6 +86,78 @@ func NewKeepaliveKeystoneClient(auth_url, tenant_name, username, password, token
}
}

// Authenticate sends an authentication request to keystone.
func (kClient *KeystoneClient) AuthenticateV3() error {
kClient.isv3Client = true
type AuthCredentialsRequestv3 struct {
Auth struct {
Identity struct {
Methods []string `json:"methods"`
Password struct {
User struct {
Domain struct {
ID string `json:"id"`
} `json:"domain"`
Name string `json:"name"`
Password string `json:"password"`
} `json:"user"`
} `json:"password"`
} `json:"identity"`
Scope struct {
System struct {
All bool `json:"all"`
} `json:"system"`
} `json:"scope"`
} `json:"auth"`
}

url := kClient.osAuthURL
if url[len(url)-1] != '/' {
url += "/"
}
url += "tokens"

var data []byte
var err error
request := AuthCredentialsRequestv3{}
request.Auth.Identity.Password.User.Name = kClient.osUsername
request.Auth.Identity.Password.User.Password = kClient.osPassword
request.Auth.Identity.Password.User.Domain.ID = "default"
request.Auth.Identity.Methods = append(request.Auth.Identity.Methods, "password")
request.Auth.Scope.System.All = true
if data, err = json.Marshal(&request); err != nil {
return err
}

resp, err := http.Post(url, "application/json",
bytes.NewReader(data))

if err != nil {
return err
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}

if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return fmt.Errorf("%s: %s", resp.Status, body)
}

var response KeystoneTokenv3
err = json.Unmarshal(body, &response)
if err != nil {
return err
}
kClient.tokenID = resp.Header.Get("X-Subject-Token")
kClient.issuedAt = response.Token.IssuedAt
kClient.expiresAt = response.Token.ExpiresAt
return nil

}

// Authenticate sends an authentication request to keystone.
func (kClient *KeystoneClient) Authenticate() error {
// identity:CredentialType
Expand All @@ -96,6 +177,7 @@ func (kClient *KeystoneClient) Authenticate() error {
} `json:"passwordCredentials"`
} `json:"auth"`
}

// identity-api/v2.0/src/xsd/token.xsd
// <element name="access" type="identity:AuthenticateResponse"/>
type TokenResponse struct {
Expand Down Expand Up @@ -129,6 +211,7 @@ func (kClient *KeystoneClient) Authenticate() error {
request.Auth.TenantName = kClient.osTenantName
data, err = json.Marshal(&request)
}

if err != nil {
return err
}
Expand Down Expand Up @@ -157,22 +240,23 @@ func (kClient *KeystoneClient) Authenticate() error {
return err
}

kClient.current = new(KeystoneToken)
*kClient.current = response.Access.Token
kClient.expiresAt = response.Access.Token.Expires
kClient.issuedAt = response.Access.Token.Issued_At
kClient.tokenID = response.Access.Token.Id
return nil
}

func (kClient *KeepaliveKeystoneClient) needsRefreshing() (bool, error) {
if kClient.current == nil {
if kClient.tokenID == "" {
return true, nil
}

issuedAtTime, err := time.Parse(time.RFC3339, kClient.current.Issued_At)
issuedAtTime, err := time.Parse(time.RFC3339, kClient.issuedAt)
if err != nil {
return false, err
}

expires, err := time.Parse(time.RFC3339, kClient.current.Expires)
expires, err := time.Parse(time.RFC3339, kClient.expiresAt)
if err != nil {
return false, err
}
Expand All @@ -190,21 +274,26 @@ func (kClient *KeepaliveKeystoneClient) AddAuthentication(req *http.Request) err
}

if needsRefreshing {
kClient.current = nil
kClient.tokenID = ""
}

return kClient.KeystoneClient.AddAuthentication(req)
}

// AddAuthentication adds the authentication token to the HTTP header.
func (kClient *KeystoneClient) AddAuthentication(req *http.Request) error {
if kClient.current == nil {
err := kClient.Authenticate()
if err != nil {
return err
if kClient.tokenID == "" {
if kClient.isv3Client {
if err := kClient.AuthenticateV3(); err != nil {
return err
}
} else {
if err := kClient.Authenticate(); err != nil {
return err
}
}
}
req.Header.Set("X-Auth-Token", kClient.current.Id)
req.Header.Set("X-Auth-Token", kClient.tokenID)
return nil
}

Expand Down