add basic JWT auth structure

This commit is contained in:
Danny van Kooten 2016-11-22 22:33:50 +01:00
parent 2ff06c02d0
commit 5d6796dfe5
8 changed files with 483 additions and 277 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules
static/assets/js
static/assets/css
.*

35
ana.go
View File

@ -3,28 +3,35 @@ package main
import (
"net/http"
"os"
"log"
"github.com/dannyvankooten/ana/core"
"github.com/dannyvankooten/ana/api"
"github.com/gorilla/mux"
"github.com/gorilla/handlers"
"github.com/joho/godotenv"
)
// TODO: Authentication.
func main() {
db := core.SetupDatabaseConnection()
defer db.Close()
// test .env file
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
r := mux.NewRouter()
db := core.SetupDatabaseConnection()
defer db.Close()
// register routes
r.HandleFunc("/collect", api.CollectHandler).Methods("GET")
r.HandleFunc("/api/visits/count/day", api.GetVisitsDayCountHandler).Methods("GET")
r.HandleFunc("/api/visits/count/realtime", api.GetVisitsRealtimeCount).Methods("GET")
r.HandleFunc("/api/visits", api.GetVisitsHandler).Methods("GET")
r.HandleFunc("/api/pageviews", api.GetPageviewsHandler).Methods("GET")
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
r.Handle("/", http.FileServer(http.Dir("./views/")))
r := mux.NewRouter()
http.ListenAndServe(":8080", handlers.LoggingHandler(os.Stdout, r))
// register routes
r.Handle("/token", api.GetTokenHandler).Methods("GET")
r.HandleFunc("/collect", api.CollectHandler).Methods("GET")
r.Handle("/api/visits/count/day", api.Authorize(api.GetVisitsDayCountHandler)).Methods("GET")
r.Handle("/api/visits/count/realtime", api.Authorize(api.GetVisitsRealtimeCount)).Methods("GET")
r.Handle("/api/visits", api.Authorize(api.GetVisitsHandler)).Methods("GET")
r.Handle("/api/pageviews", api.Authorize(api.GetPageviewsHandler)).Methods("GET")
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static/"))))
r.Handle("/", http.FileServer(http.Dir("./views/")))
http.ListenAndServe(":8080", handlers.LoggingHandler(os.Stdout, r))
}

51
api/auth.go Normal file
View File

@ -0,0 +1,51 @@
package api
import (
"net/http"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
"os"
"time"
)
func Authorize(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor, keyLookupFunc)
if err == nil && token.Valid {
next.ServeHTTP(w, r)
return
}
w.WriteHeader(http.StatusUnauthorized)
})
}
func getSigningKey() []byte {
return []byte(os.Getenv("APP_SECRET_KEY"))
}
/* Handlers */
var GetTokenHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
// TODO: Check with database here.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"admin": true,
"name": "Danny",
"exp": time.Now().Add(time.Hour * 24).Unix(),
})
/* Sign the token with our secret */
tokenString, _ := token.SignedString(getSigningKey())
/* Finally, write the token to the browser window */
w.Write([]byte(tokenString))
})
func keyLookupFunc(t *jwt.Token) (interface{}, error) {
// Look up key in database.
// TODO
//
return getSigningKey(), nil
}

View File

@ -8,7 +8,7 @@ import (
)
// URL: /api/pageviews
func GetPageviewsHandler(w http.ResponseWriter, r *http.Request) {
var GetPageviewsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
stmt, err := core.DB.Prepare(`SELECT
path,
COUNT(ip_address) AS pageviews,
@ -35,4 +35,4 @@ func GetPageviewsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}
})

View File

@ -9,7 +9,7 @@ import (
)
// URL: /api/visits
func GetVisitsHandler(w http.ResponseWriter, r *http.Request) {
var GetVisitsHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
stmt, err := core.DB.Prepare(`SELECT
id,
COALESCE(browser_name, '') AS browser_name,
@ -48,20 +48,20 @@ func GetVisitsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}
})
// URL: /api/visits/count/realtime
func GetVisitsRealtimeCount(w http.ResponseWriter, r *http.Request) {
var GetVisitsRealtimeCount = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
row := core.DB.QueryRow(`SELECT COUNT(DISTINCT(ip_address)) FROM visits WHERE timestamp >= DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 3 HOUR_MINUTE)`)
var result int
row.Scan(&result)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}
})
// URL: /api/visits/count/day
func GetVisitsDayCountHandler(w http.ResponseWriter, r *http.Request) {
var GetVisitsDayCountHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
stmt, err := core.DB.Prepare(`SELECT
COUNT(*) AS count, DATE_FORMAT(timestamp, '%Y-%m-%d') AS date_group
FROM visits
@ -91,4 +91,4 @@ func GetVisitsDayCountHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(results)
}
})

View File

@ -0,0 +1,21 @@
import React, { Component } from 'react'
class Login extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="block">
<h2>Login</h2>
<p>
<a href="">Sign in</a>
</p>
</div>
);
}
}
export default Login

View File

@ -6,13 +6,37 @@ import RealtimeVisitsCount from './components/realtime-visits.js';
import VisitsList from './components/visits-list.js';
import PageviewsList from './components/pageviews.js';
import VisitsGraph from './components/visits-graph.js';
import Login from './components/login.js';
class App extends React.Component {
constructor(props) {
super(props)
this.state = { idToken: null }
}
render() {
if(this.state.idToken) {
return (
<div className="container">
<h1>Ana</h1>
<RealtimeVisitsCount />
<VisitsGraph />
<PageviewsList />
</div>
);
} else {
return (
<div className="container">
<Login />
</div>
);
}
}
}
ReactDOM.render(
<div className="container">
<h1>Ana</h1>
<RealtimeVisitsCount />
<VisitsGraph />
<PageviewsList />
</div>,
<App />,
document.getElementById('root')
);

File diff suppressed because one or more lines are too long