-
Notifications
You must be signed in to change notification settings - Fork 645
/
user.go
181 lines (150 loc) · 5.1 KB
/
user.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
package handlers
import (
"context"
"net/http"
"github.com/ardanlabs/service/internal/platform/auth"
"github.com/ardanlabs/service/internal/platform/web"
"github.com/ardanlabs/service/internal/user"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"go.opencensus.io/trace"
)
// User represents the User API method handler set.
type User struct {
db *sqlx.DB
authenticator *auth.Authenticator
// ADD OTHER STATE LIKE THE LOGGER AND CONFIG HERE.
}
// List returns all the existing users in the system.
func (u *User) List(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctx, span := trace.StartSpan(ctx, "handlers.User.List")
defer span.End()
users, err := user.List(ctx, u.db)
if err != nil {
return err
}
return web.Respond(ctx, w, users, http.StatusOK)
}
// Retrieve returns the specified user from the system.
func (u *User) Retrieve(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctx, span := trace.StartSpan(ctx, "handlers.User.Retrieve")
defer span.End()
claims, ok := ctx.Value(auth.Key).(auth.Claims)
if !ok {
return errors.New("claims missing from context")
}
usr, err := user.Retrieve(ctx, claims, u.db, params["id"])
if err != nil {
switch err {
case user.ErrInvalidID:
return web.NewRequestError(err, http.StatusBadRequest)
case user.ErrNotFound:
return web.NewRequestError(err, http.StatusNotFound)
case user.ErrForbidden:
return web.NewRequestError(err, http.StatusForbidden)
default:
return errors.Wrapf(err, "Id: %s", params["id"])
}
}
return web.Respond(ctx, w, usr, http.StatusOK)
}
// Create inserts a new user into the system.
func (u *User) Create(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctx, span := trace.StartSpan(ctx, "handlers.User.Create")
defer span.End()
v, ok := ctx.Value(web.KeyValues).(*web.Values)
if !ok {
return web.NewShutdownError("web value missing from context")
}
var nu user.NewUser
if err := web.Decode(r, &nu); err != nil {
return errors.Wrap(err, "")
}
usr, err := user.Create(ctx, u.db, nu, v.Now)
if err != nil {
return errors.Wrapf(err, "User: %+v", &usr)
}
return web.Respond(ctx, w, usr, http.StatusCreated)
}
// Update updates the specified user in the system.
func (u *User) Update(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctx, span := trace.StartSpan(ctx, "handlers.User.Update")
defer span.End()
v, ok := ctx.Value(web.KeyValues).(*web.Values)
if !ok {
return web.NewShutdownError("web value missing from context")
}
claims, ok := ctx.Value(auth.Key).(auth.Claims)
if !ok {
return errors.New("claims missing from context")
}
var upd user.UpdateUser
if err := web.Decode(r, &upd); err != nil {
return errors.Wrap(err, "")
}
err := user.Update(ctx, claims, u.db, params["id"], upd, v.Now)
if err != nil {
switch err {
case user.ErrInvalidID:
return web.NewRequestError(err, http.StatusBadRequest)
case user.ErrNotFound:
return web.NewRequestError(err, http.StatusNotFound)
case user.ErrForbidden:
return web.NewRequestError(err, http.StatusForbidden)
default:
return errors.Wrapf(err, "ID: %s User: %+v", params["id"], &upd)
}
}
return web.Respond(ctx, w, nil, http.StatusNoContent)
}
// Delete removes the specified user from the system.
func (u *User) Delete(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctx, span := trace.StartSpan(ctx, "handlers.User.Delete")
defer span.End()
err := user.Delete(ctx, u.db, params["id"])
if err != nil {
switch err {
case user.ErrInvalidID:
return web.NewRequestError(err, http.StatusBadRequest)
case user.ErrNotFound:
return web.NewRequestError(err, http.StatusNotFound)
case user.ErrForbidden:
return web.NewRequestError(err, http.StatusForbidden)
default:
return errors.Wrapf(err, "Id: %s", params["id"])
}
}
return web.Respond(ctx, w, nil, http.StatusNoContent)
}
// Token handles a request to authenticate a user. It expects a request using
// Basic Auth with a user's email and password. It responds with a JWT.
func (u *User) Token(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error {
ctx, span := trace.StartSpan(ctx, "handlers.User.Token")
defer span.End()
v, ok := ctx.Value(web.KeyValues).(*web.Values)
if !ok {
return web.NewShutdownError("web value missing from context")
}
email, pass, ok := r.BasicAuth()
if !ok {
err := errors.New("must provide email and password in Basic auth")
return web.NewRequestError(err, http.StatusUnauthorized)
}
claims, err := user.Authenticate(ctx, u.db, v.Now, email, pass)
if err != nil {
switch err {
case user.ErrAuthenticationFailure:
return web.NewRequestError(err, http.StatusUnauthorized)
default:
return errors.Wrap(err, "authenticating")
}
}
var tkn struct {
Token string `json:"token"`
}
tkn.Token, err = u.authenticator.GenerateToken(claims)
if err != nil {
return errors.Wrap(err, "generating token")
}
return web.Respond(ctx, w, tkn, http.StatusOK)
}