Skip to content

Commit 9018b2e

Browse files
committed
Change non-authboss pieces into JSON
- Add flag "-api" that allows the server to serve and receive JSON instead of HTML/forms.
1 parent b1080c5 commit 9018b2e

File tree

4 files changed

+132
-40
lines changed

4 files changed

+132
-40
lines changed

blog.go

+111-33
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package main
33
import (
44
"context"
55
"encoding/base64"
6+
"encoding/json"
67
"flag"
78
"fmt"
89
"html/template"
10+
"io/ioutil"
911
"log"
1012
"net/http"
1113
"os"
@@ -48,13 +50,14 @@ var (
4850
flagDebug = flag.Bool("debug", false, "output debugging information")
4951
flagDebugDB = flag.Bool("debugdb", false, "output database on each request")
5052
flagDebugCTX = flag.Bool("debugctx", false, "output specific authboss related context keys on each request")
53+
flagAPI = flag.Bool("api", false, "configure the app to be an api instead of an html app")
5154
)
5255

5356
var (
54-
ab = authboss.New()
55-
database = NewMemStorer()
56-
templates = tpl.Must(tpl.Load("views", "views/partials", "layout.html.tpl", funcs))
57-
schemaDec = schema.NewDecoder()
57+
ab = authboss.New()
58+
database = NewMemStorer()
59+
schemaDec = schema.NewDecoder()
60+
templates tpl.Templates = nil
5861
)
5962

6063
func setupAuthboss() {
@@ -132,6 +135,10 @@ func setupAuthboss() {
132135
func main() {
133136
flag.Parse()
134137

138+
if !*flagAPI {
139+
templates = tpl.Must(tpl.Load("views", "views/partials", "layout.html.tpl", funcs))
140+
}
141+
135142
// Initialize Sessions and Cookies
136143
// Typically gorilla securecookie and sessions packages require
137144
// highly random secret keys that are not divulged to the public.
@@ -197,26 +204,29 @@ func main() {
197204

198205
func dataInjector(handler http.Handler) http.Handler {
199206
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
200-
data := layoutData(w, r)
207+
data := layoutData(w, &r)
201208
r = r.WithContext(context.WithValue(r.Context(), authboss.CTXKeyData, data))
202209
handler.ServeHTTP(w, r)
203210
})
204211
}
205212

206-
func layoutData(w http.ResponseWriter, r *http.Request) authboss.HTMLData {
213+
// layoutData is passing pointers to pointers be able to edit the current pointer
214+
// to the request. This is still safe as it still creates a new request and doesn't
215+
// modify the old one, it just modifies what we're pointing to in our methods so
216+
// we're able to skip returning an *http.Request everywhere
217+
func layoutData(w http.ResponseWriter, r **http.Request) authboss.HTMLData {
207218
currentUserName := ""
208-
userInter, err := ab.CurrentUser(r)
219+
userInter, err := ab.LoadCurrentUser(r)
209220
if userInter != nil && err == nil {
210221
currentUserName = userInter.(*User).Name
211222
}
212223

213224
return authboss.HTMLData{
214225
"loggedin": userInter != nil,
215-
"username": "",
216226
"current_user_name": currentUserName,
217-
"csrf_token": nosurf.Token(r),
218-
"flash_success": authboss.FlashSuccess(w, r),
219-
"flash_error": authboss.FlashError(w, r),
227+
"csrf_token": nosurf.Token(*r),
228+
"flash_success": authboss.FlashSuccess(w, *r),
229+
"flash_error": authboss.FlashError(w, *r),
220230
}
221231
}
222232

@@ -231,16 +241,27 @@ func newblog(w http.ResponseWriter, r *http.Request) {
231241
var nextID = len(blogs) + 1
232242

233243
func create(w http.ResponseWriter, r *http.Request) {
234-
err := r.ParseForm()
235-
if badRequest(w, err) {
236-
return
237-
}
238-
239244
// TODO: Validation
240245

241246
var b Blog
242-
if badRequest(w, schemaDec.Decode(&b, r.PostForm)) {
243-
return
247+
if *flagAPI {
248+
byt, err := ioutil.ReadAll(r.Body)
249+
if badRequest(w, err) {
250+
return
251+
}
252+
253+
if badRequest(w, json.Unmarshal(byt, &b)) {
254+
return
255+
}
256+
} else {
257+
err := r.ParseForm()
258+
if badRequest(w, err) {
259+
return
260+
}
261+
262+
if badRequest(w, schemaDec.Decode(&b, r.PostForm)) {
263+
return
264+
}
244265
}
245266

246267
abuser := ab.CurrentUserP(r)
@@ -253,7 +274,12 @@ func create(w http.ResponseWriter, r *http.Request) {
253274

254275
blogs = append(blogs, b)
255276

256-
http.Redirect(w, r, "/", http.StatusFound)
277+
if *flagAPI {
278+
w.WriteHeader(http.StatusOK)
279+
return
280+
}
281+
282+
redirect(w, r, "/")
257283
}
258284

259285
func edit(w http.ResponseWriter, r *http.Request) {
@@ -262,16 +288,10 @@ func edit(w http.ResponseWriter, r *http.Request) {
262288
return
263289
}
264290

265-
data := layoutData(w, r).MergeKV("post", blogs.Get(id))
266-
mustRender(w, r, "edit", data)
291+
mustRender(w, r, "edit", authboss.HTMLData{"post": blogs.Get(id)})
267292
}
268293

269294
func update(w http.ResponseWriter, r *http.Request) {
270-
err := r.ParseForm()
271-
if badRequest(w, err) {
272-
return
273-
}
274-
275295
id, ok := blogID(w, r)
276296
if !ok {
277297
return
@@ -280,13 +300,34 @@ func update(w http.ResponseWriter, r *http.Request) {
280300
// TODO: Validation
281301

282302
var b = blogs.Get(id)
283-
if badRequest(w, schemaDec.Decode(b, r.PostForm)) {
284-
return
303+
304+
if *flagAPI {
305+
byt, err := ioutil.ReadAll(r.Body)
306+
if badRequest(w, err) {
307+
return
308+
}
309+
310+
if badRequest(w, json.Unmarshal(byt, &b)) {
311+
return
312+
}
313+
} else {
314+
err := r.ParseForm()
315+
if badRequest(w, err) {
316+
return
317+
}
318+
if badRequest(w, schemaDec.Decode(b, r.PostForm)) {
319+
return
320+
}
285321
}
286322

287323
b.Date = time.Now()
288324

289-
http.Redirect(w, r, "/", http.StatusFound)
325+
if *flagAPI {
326+
w.WriteHeader(http.StatusOK)
327+
return
328+
}
329+
330+
redirect(w, r, "/")
290331
}
291332

292333
func destroy(w http.ResponseWriter, r *http.Request) {
@@ -297,7 +338,12 @@ func destroy(w http.ResponseWriter, r *http.Request) {
297338

298339
blogs.Delete(id)
299340

300-
http.Redirect(w, r, "/", http.StatusFound)
341+
if *flagAPI {
342+
w.WriteHeader(http.StatusOK)
343+
return
344+
}
345+
346+
redirect(w, r, "/")
301347
}
302348

303349
func blogID(w http.ResponseWriter, r *http.Request) (int, bool) {
@@ -306,12 +352,12 @@ func blogID(w http.ResponseWriter, r *http.Request) (int, bool) {
306352
id, err := strconv.Atoi(str)
307353
if err != nil {
308354
log.Println("Error parsing blog id:", err)
309-
http.Redirect(w, r, "/", http.StatusFound)
355+
redirect(w, r, "/")
310356
return 0, false
311357
}
312358

313359
if id <= 0 {
314-
http.Redirect(w, r, "/", http.StatusFound)
360+
redirect(w, r, "/")
315361
return 0, false
316362
}
317363

@@ -333,6 +379,20 @@ func mustRender(w http.ResponseWriter, r *http.Request, name string, data authbo
333379
current.MergeKV("csrf_token", nosurf.Token(r))
334380
current.Merge(data)
335381

382+
if *flagAPI {
383+
w.Header().Set("Content-Type", "application/json")
384+
385+
byt, err := json.Marshal(current)
386+
if err != nil {
387+
w.WriteHeader(http.StatusInternalServerError)
388+
fmt.Println("failed to marshal json:", err)
389+
fmt.Fprintln(w, `{"error":"internal server error"}`)
390+
}
391+
392+
w.Write(byt)
393+
return
394+
}
395+
336396
err := templates.Render(w, name, current)
337397
if err == nil {
338398
return
@@ -343,14 +403,32 @@ func mustRender(w http.ResponseWriter, r *http.Request, name string, data authbo
343403
fmt.Fprintln(w, "Error occurred rendering template:", err)
344404
}
345405

406+
func redirect(w http.ResponseWriter, r *http.Request, path string) {
407+
if *flagAPI {
408+
w.Header().Set("Content-Type", "application/json")
409+
w.Header().Set("Location", path)
410+
w.WriteHeader(http.StatusFound)
411+
fmt.Fprintf(w, `{"path": %q}`, path)
412+
return
413+
}
414+
415+
http.Redirect(w, r, path, http.StatusFound)
416+
}
417+
346418
func badRequest(w http.ResponseWriter, err error) bool {
347419
if err == nil {
348420
return false
349421
}
350422

423+
if *flagAPI {
424+
w.Header().Set("Content-Type", "application/json")
425+
w.WriteHeader(http.StatusBadRequest)
426+
fmt.Fprintln(w, `{"error":"bad request"}`, err)
427+
return true
428+
}
429+
351430
w.Header().Set("Content-Type", "text/plain")
352431
w.WriteHeader(http.StatusBadRequest)
353432
fmt.Fprintln(w, "Bad request:", err)
354-
355433
return true
356434
}

blogs.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import "time"
44

55
// Blog data
66
type Blog struct {
7-
ID int
8-
Title string
9-
AuthorID string
10-
Date time.Time
11-
Content string
7+
ID int `json:"id,omitempty"`
8+
Title string `json:"title,omitempty"`
9+
AuthorID string `json:"author_id,omitempty"`
10+
Date time.Time `json:"date,omitempty"`
11+
Content string `json:"content,omitempty"`
1212
}
1313

1414
// Blogs storage

storer.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,19 @@ func (m MemStorer) Save(ctx context.Context, user authboss.User) error {
184184

185185
// Load the user
186186
func (m MemStorer) Load(ctx context.Context, key string) (user authboss.User, err error) {
187+
// Check to see if our key is actually an oauth2 pid
188+
provider, uid, err := authboss.ParseOAuth2PID(key)
189+
if err == nil {
190+
for _, u := range m.Users {
191+
if u.OAuth2Provider == provider && u.OAuth2UID == uid {
192+
debugln("Loaded OAuth2 user:", u.Email)
193+
return &u, nil
194+
}
195+
}
196+
197+
return nil, authboss.ErrUserNotFound
198+
}
199+
187200
u, ok := m.Users[key]
188201
if !ok {
189202
return nil, authboss.ErrUserNotFound
@@ -264,7 +277,7 @@ func (m MemStorer) UseRememberToken(pid, token string) error {
264277
if tok == token {
265278
tokens[len(tokens)-1] = tokens[i]
266279
m.Tokens[pid] = tokens[:len(tokens)-1]
267-
debugf("Used remember for %s: %s", pid, token)
280+
debugf("Used remember for %s: %s\n", pid, token)
268281
return nil
269282
}
270283
}
@@ -274,7 +287,6 @@ func (m MemStorer) UseRememberToken(pid, token string) error {
274287

275288
// NewFromOAuth2 creates an oauth2 user (but not in the database, just a blank one to be saved later)
276289
func (m MemStorer) NewFromOAuth2(ctx context.Context, provider string, details map[string]string) (authboss.OAuth2User, error) {
277-
278290
switch provider {
279291
case "google":
280292
email := details[aboauth.OAuth2Email]
@@ -292,6 +304,7 @@ func (m MemStorer) NewFromOAuth2(ctx context.Context, provider string, details m
292304
user.Name = "Unknown"
293305
user.Email = details[aboauth.OAuth2Email]
294306
user.OAuth2UID = details[aboauth.OAuth2UID]
307+
user.Confirmed = true
295308

296309
return user, nil
297310
}

views/layout.html.tpl

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<li><a href="/auth/register">Register</a></li>
2626
<li><a href="/auth/recover">Recover</a></li>
2727
<li><a href="/auth/login"><i class="fa fa-sign-in"></i> Login</a></li>
28+
<li><a href="/auth/oauth2/google"><i class="fa fa-google"></i> Google Login</a></li>
2829
{{else}}
2930
<li class="dropdown">
3031
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Welcome {{.current_user_name}}! <span class="caret"></span></a>

0 commit comments

Comments
 (0)