diff --git a/client.go b/client.go index e4971f8..3bc3d60 100644 --- a/client.go +++ b/client.go @@ -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{ @@ -118,6 +120,7 @@ type Client struct { port int httpClient *http.Client auth Authenticator + encrypt Encryptor } type TlsConfig struct { @@ -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 } @@ -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 diff --git a/keystone.go b/keystone.go index 43def02..ab77e0c 100644 --- a/keystone.go +++ b/keystone.go @@ -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 @@ -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{ @@ -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 @@ -96,6 +177,7 @@ func (kClient *KeystoneClient) Authenticate() error { } `json:"passwordCredentials"` } `json:"auth"` } + // identity-api/v2.0/src/xsd/token.xsd // type TokenResponse struct { @@ -129,6 +211,7 @@ func (kClient *KeystoneClient) Authenticate() error { request.Auth.TenantName = kClient.osTenantName data, err = json.Marshal(&request) } + if err != nil { return err } @@ -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 } @@ -190,7 +274,7 @@ func (kClient *KeepaliveKeystoneClient) AddAuthentication(req *http.Request) err } if needsRefreshing { - kClient.current = nil + kClient.tokenID = "" } return kClient.KeystoneClient.AddAuthentication(req) @@ -198,13 +282,18 @@ func (kClient *KeepaliveKeystoneClient) AddAuthentication(req *http.Request) err // 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 }