~rogpeppe/goose/state-of-the-world

« back to all changes in this revision

Viewing changes to identity/userpass.go

  • Committer: Ian Booth
  • Date: 2012-11-21 07:56:19 UTC
  • Revision ID: ian.booth@canonical.com-20121121075619-4fh6i9yq6fj6cwct
Extract identity functionality from client, and also extract common HTTP methods

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
package identity
2
2
 
3
3
import (
4
 
        "bytes"
5
 
        "encoding/json"
6
4
        "fmt"
7
 
        "io/ioutil"
8
5
        "net/http"
 
6
        goosehttp "launchpad.net/goose/http"
9
7
)
10
8
 
11
 
type ErrorResponse struct {
12
 
        Message string `json:"message"`
13
 
        Code    int    `json:"code"`
14
 
        Title   string `json:"title"`
15
 
}
16
 
 
17
 
func (e *ErrorResponse) Error() string {
18
 
        return fmt.Sprintf("Failed: %d %s: %s", e.Code, e.Title, e.Message)
19
 
}
20
 
 
21
 
type ErrorWrapper struct {
22
 
        Error ErrorResponse `json:"error"`
23
 
}
24
 
 
25
9
type PasswordCredentials struct {
26
10
        Username string `json:"username"`
27
11
        Password string `json:"password"`
43
27
        Region      string `json:"region"`
44
28
}
45
29
 
46
 
type Service struct {
 
30
type ServiceResponse struct {
47
31
        Name      string `json:"name"`
48
32
        Type      string `json:"type"`
49
33
        Endpoints []Endpoint
55
39
        Tenant  struct {
56
40
                Id   string `json:"id"`
57
41
                Name string `json:"name"`
 
42
                Description string `json:"description"`
 
43
                Enabled bool `json:"enabled"`
58
44
        } `json:"tenant"`
59
45
}
60
46
 
75
61
}
76
62
 
77
63
type AccessResponse struct {
78
 
        ServiceCatalog []Service     `json:"serviceCatalog"`
 
64
        ServiceCatalog []ServiceResponse `json:"serviceCatalog"`
79
65
        Token          TokenResponse `json:"token"`
80
66
        User           UserResponse  `json:"user"`
81
67
}
82
68
 
83
69
type UserPass struct {
 
70
        client *goosehttp.GooseHTTPClient
84
71
}
85
72
 
86
 
func (l *UserPass) Auth(creds Credentials) (*AuthDetails, error) {
87
 
        client := &http.Client{}
 
73
func (u *UserPass) Auth(creds *Credentials) (*AuthDetails, error) {
 
74
        if u.client == nil {
 
75
                u.client = &goosehttp.GooseHTTPClient{http.Client{CheckRedirect: nil}}
 
76
        }
88
77
        auth := AuthWrapper{Auth: AuthRequest{
89
78
                PasswordCredentials: PasswordCredentials{
90
79
                        Username: creds.User,
91
80
                        Password: creds.Secrets,
92
81
                },
93
 
                TenantName: "tenant-name"}}
94
 
        auth_json, err := json.Marshal(auth)
95
 
        request, err := http.NewRequest("POST", creds.URL+"/tokens", bytes.NewBuffer(auth_json))
96
 
        if err != nil {
97
 
                return nil, err
98
 
        }
99
 
        request.Header.Set("Content-Type", "application/json")
100
 
        response, err := client.Do(request)
101
 
        if err != nil {
102
 
                return nil, err
103
 
        }
104
 
        content, err := ioutil.ReadAll(response.Body)
105
 
        response.Body.Close()
106
 
        if err != nil {
107
 
                return nil, err
108
 
        }
109
 
        if response.StatusCode != http.StatusOK {
110
 
                // Check if we have a JSON representation of the failure, if so, return it
111
 
                if response.Header.Get("Content-Type") == "application/json" {
112
 
                        var wrappedErr ErrorWrapper
113
 
                        if err := json.Unmarshal(content, &wrappedErr); err == nil {
114
 
                                return nil, &wrappedErr.Error
 
82
                TenantName: creds.TenantName}}
 
83
 
 
84
        var accessWrapper AccessWrapper
 
85
        requestData := goosehttp.RequestData{ReqValue: auth, RespValue: &accessWrapper}
 
86
        err := u.client.JsonRequest("POST", creds.URL, &requestData)
 
87
        if err != nil {
 
88
                return nil, err
 
89
        }
 
90
 
 
91
        details := &AuthDetails{}
 
92
        access := accessWrapper.Access
 
93
        respToken := access.Token
 
94
        if respToken.Id == "" {
 
95
                return nil, fmt.Errorf("Did not get valid Token from auth request")
 
96
        }
 
97
        details.TokenId = respToken.Id
 
98
        details.TenantId = respToken.Tenant.Id
 
99
        details.UserId = access.User.Id
 
100
        details.ServiceURLs = make(map[string]string, len(access.ServiceCatalog))
 
101
        for _, service := range access.ServiceCatalog {
 
102
                for i, e := range service.Endpoints {
 
103
                        if e.Region != creds.Region {
 
104
                                service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...)
115
105
                        }
116
106
                }
117
 
                // We weren't able to parse the response, so just return our own error
118
 
                return nil, fmt.Errorf("Failed to Authenticate (code %d %s): %s",
119
 
                        response.StatusCode, response.Status, content)
120
 
        }
121
 
        if response.Header.Get("Content-Type") != "application/json" {
122
 
                return nil, fmt.Errorf("Failed to Authenticate. Did not get JSON back: %s", content)
123
 
        }
124
 
        var access AccessWrapper
125
 
        if err := json.Unmarshal(content, &access); err != nil {
126
 
                return nil, err
127
 
        }
128
 
        details := &AuthDetails{}
129
 
        details.Token = access.Access.Token.Id
130
 
        if details.Token == "" {
131
 
                return nil, fmt.Errorf("Did not get valid Token from auth request")
132
 
        }
133
 
        details.ServiceURLs = make(map[string]string, len(access.Access.ServiceCatalog))
134
 
        for _, service := range access.Access.ServiceCatalog {
135
107
                details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL
136
108
        }
137
109