forked from jarias/stormpath-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
application.go
279 lines (223 loc) · 8.57 KB
/
application.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
package stormpath
import (
"encoding/base64"
"errors"
"net/url"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/nu7hatch/gouuid"
)
//Application represents a Stormpath application object
//
//See: http://docs.stormpath.com/rest/product-guide/#applications
type Application struct {
accountStoreResource
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Status string `json:"status,omitempty"`
Groups *Groups `json:"groups,omitempty"`
Tenant *Tenant `json:"tenant,omitempty"`
PasswordResetTokens *resource `json:"passwordResetTokens,omitempty"`
AccountStoreMappings *AccountStoreMappings `json:"accountStoreMappings,omitempty"`
DefaultAccountStoreMapping *AccountStoreMapping `json:"defaultAccountStoreMapping,omitempty"`
DefaultGroupStoreMapping *AccountStoreMapping `json:"defaultGroupStoreMapping,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
ModifiedAt time.Time `json:"modifiedAt,omitempty"`
}
//Applications represents a paged result or applications
type Applications struct {
collectionResource
Items []Application `json:"items"`
}
//IDSiteCallbackResult holds the ID Site callback parsed JWT token information + the acccount if one was given
type IDSiteCallbackResult struct {
Account *Account
State string
IsNew bool
Status string
}
//NewApplication creates a new application
func NewApplication(name string) *Application {
return &Application{Name: name}
}
func GetApplication(href string, criteria Criteria) (*Application, error) {
application := &Application{}
err := client.get(
buildAbsoluteURL(href, criteria.ToQueryString()),
emptyPayload(),
application,
)
return application, err
}
//Purge deletes all the account stores before deleting the application
//
//See: http://docs.stormpath.com/rest/product-guide/#delete-an-application
func (app *Application) Purge() error {
accountStoreMappings, err := app.GetAccountStoreMappings(MakeAccountStoreMappingCriteria().Offset(0).Limit(25))
if err != nil {
return err
}
for _, m := range accountStoreMappings.Items {
client.delete(m.AccountStore.Href, emptyPayload())
}
return app.Delete()
}
//GetAccountStoreMappings returns all the applications account store mappings
//
//See: http://docs.stormpath.com/rest/product-guide/#application-account-store-mappings
func (app *Application) GetAccountStoreMappings(criteria Criteria) (*AccountStoreMappings, error) {
accountStoreMappings := &AccountStoreMappings{}
err := client.get(
buildAbsoluteURL(app.AccountStoreMappings.Href, criteria.ToQueryString()),
emptyPayload(),
accountStoreMappings,
)
return accountStoreMappings, err
}
//RegisterAccount registers a new account into the application
//
//See: http://docs.stormpath.com/rest/product-guide/#application-accounts
func (app *Application) RegisterAccount(account *Account) error {
err := client.post(app.Accounts.Href, account, account)
if err == nil {
//Password should be cleanup so we don't keep an unhash password in memory
account.Password = ""
}
return err
}
//RegisterSocialAccount registers a new account into the application using an external provider Google, Facebook
//
//See: http://docs.stormpath.com/rest/product-guide/#accessing-accounts-with-google-authorization-codes-or-an-access-tokens
func (app *Application) RegisterSocialAccount(socialAccount *SocialAccount) (*Account, error) {
account := &Account{}
err := client.post(app.Accounts.Href, socialAccount, account)
return account, err
}
//AuthenticateAccount authenticates an account against the application
//
//See: http://docs.stormpath.com/rest/product-guide/#authenticate-an-account
func (app *Application) AuthenticateAccount(username string, password string) (*Account, error) {
accountRef := &accountRef{}
account := &Account{}
loginAttemptPayload := make(map[string]string)
loginAttemptPayload["type"] = "basic"
loginAttemptPayload["value"] = base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
err := client.post(buildAbsoluteURL(app.Href, "loginAttempts"), loginAttemptPayload, accountRef)
if err != nil {
return nil, err
}
account.Href = accountRef.Account.Href
return account, err
}
//SendPasswordResetEmail sends a password reset email to the given user
//
//See: http://docs.stormpath.com/rest/product-guide/#reset-an-accounts-password
func (app *Application) SendPasswordResetEmail(username string) (*AccountPasswordResetToken, error) {
passwordResetToken := &AccountPasswordResetToken{}
passwordResetPayload := make(map[string]string)
passwordResetPayload["email"] = username
err := client.post(buildAbsoluteURL(app.Href, "passwordResetTokens"), passwordResetPayload, passwordResetToken)
return passwordResetToken, err
}
//ValidatePasswordResetToken validates a password reset token
//
//See: http://docs.stormpath.com/rest/product-guide/#reset-an-accounts-password
func (app *Application) ValidatePasswordResetToken(token string) (*AccountPasswordResetToken, error) {
passwordResetToken := &AccountPasswordResetToken{}
err := client.get(buildAbsoluteURL(app.Href, "passwordResetTokens", token), emptyPayload(), passwordResetToken)
return passwordResetToken, err
}
//ResetPassword resets a user password based on the reset token
//
//See: http://docs.stormpath.com/rest/product-guide/#reset-an-accounts-password
func (app *Application) ResetPassword(token string, newPassword string) (*Account, error) {
accountRef := &accountRef{}
account := &Account{}
resetPasswordPayload := make(map[string]string)
resetPasswordPayload["password"] = newPassword
err := client.post(buildAbsoluteURL(app.Href, "passwordResetTokens", token), resetPasswordPayload, accountRef)
if err != nil {
return nil, err
}
account.Href = accountRef.Account.Href
return account, err
}
//CreateGroup creates a new group in the application
//
//See: http://docs.stormpath.com/rest/product-guide/#application-groups
func (app *Application) CreateGroup(group *Group) error {
return client.post(app.Groups.Href, group, group)
}
//GetGroups returns all the application groups
//
//See: http://docs.stormpath.com/rest/product-guide/#application-groups
func (app *Application) GetGroups(criteria Criteria) (*Groups, error) {
groups := &Groups{}
err := client.get(
buildAbsoluteURL(app.Groups.Href, criteria.ToQueryString()),
emptyPayload(),
groups,
)
return groups, err
}
//CreateIDSiteURL creates the IDSite URL for the application
func (app *Application) CreateIDSiteURL(options map[string]string) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
nonce, _ := uuid.NewV4()
if options["path"] == "" {
options["path"] = "/"
}
token.Claims["jti"] = nonce.String()
token.Claims["iat"] = time.Now().Unix()
token.Claims["iss"] = client.Credentials.ID
token.Claims["sub"] = app.Href
token.Claims["state"] = options["state"]
token.Claims["path"] = options["path"]
token.Claims["cb_uri"] = options["callbackURI"]
tokenString, err := token.SignedString([]byte(client.Credentials.Secret))
if err != nil {
return "", err
}
p, _ := url.Parse(app.Href)
ssoURL := p.Scheme + "://" + p.Host + "/sso"
if options["logout"] == "true" {
ssoURL = ssoURL + "/logout" + "?jwtRequest=" + tokenString
} else {
ssoURL = ssoURL + "?jwtRequest=" + tokenString
}
return ssoURL, nil
}
//HandleIDSiteCallback handles the URL from an ID Site callback it parses the JWT token
//validates it and return an IDSiteCallbackResult with the token info + the Account if the sub was given
func (app *Application) HandleIDSiteCallback(URL string) (*IDSiteCallbackResult, error) {
result := &IDSiteCallbackResult{}
cbURL, err := url.Parse(URL)
if err != nil {
return nil, err
}
jwtResponse := cbURL.Query().Get("jwtResponse")
token, err := jwt.Parse(jwtResponse, func(token *jwt.Token) (interface{}, error) {
return []byte(client.Credentials.Secret), nil
})
if err != nil {
return nil, err
}
if token.Claims["aud"].(string) != client.Credentials.ID {
return nil, errors.New("ID Site invalid aud")
}
if time.Now().Unix() > int64(token.Claims["exp"].(float64)) {
return nil, errors.New("ID Site JWT has expired")
}
if token.Claims["sub"] != nil {
account, err := GetAccount(token.Claims["sub"].(string), MakeAccountCriteria())
if err != nil {
return nil, err
}
result.Account = account
}
if token.Claims["state"] != nil {
result.State = token.Claims["state"].(string)
}
result.Status = token.Claims["status"].(string)
return result, nil
}