feat: add pre-commit hooks & refactor code to pass linting

This commit is contained in:
Jordan Knott
2020-08-20 18:11:24 -05:00
parent abf9e1328a
commit 9dba566660
49 changed files with 297 additions and 462 deletions

View File

@ -18,11 +18,13 @@ var jwtKey = []byte("taskcafe_test_key")
type authResource struct{}
// LoginRequestData is the request data when a user logs in
type LoginRequestData struct {
Username string
Password string
}
// NewUserAccount is the request data for a new user
type NewUserAccount struct {
FullName string `json:"fullname"`
Username string
@ -31,30 +33,35 @@ type NewUserAccount struct {
Email string
}
// InstallRequestData is the request data for installing new Taskcafe app
type InstallRequestData struct {
User NewUserAccount
}
// LoginResponseData is the response data for when a user logs in
type LoginResponseData struct {
AccessToken string `json:"accessToken"`
IsInstalled bool `json:"isInstalled"`
}
// LogoutResponseData is the response data for when a user logs out
type LogoutResponseData struct {
Status string `json:"status"`
}
// RefreshTokenResponseData is the response data for when an access token is refreshed
type RefreshTokenResponseData struct {
AccessToken string `json:"accessToken"`
}
// AvatarUploadResponseData is the response data for a user profile is uploaded
type AvatarUploadResponseData struct {
UserID string `json:"userID"`
URL string `json:"url"`
}
// RefreshTokenHandler handles when a user attempts to refresh token
func (h *TaskcafeHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Request) {
_, err := h.repo.GetSystemOptionByKey(r.Context(), "is_installed")
if err == sql.ErrNoRows {
user, err := h.repo.GetUserAccountByUsername(r.Context(), "system")
@ -131,6 +138,7 @@ func (h *TaskcafeHandler) RefreshTokenHandler(w http.ResponseWriter, r *http.Req
json.NewEncoder(w).Encode(LoginResponseData{AccessToken: accessTokenString, IsInstalled: true})
}
// LogoutHandler removes all refresh tokens to log out user
func (h *TaskcafeHandler) LogoutHandler(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("refreshToken")
if err != nil {
@ -150,6 +158,7 @@ func (h *TaskcafeHandler) LogoutHandler(w http.ResponseWriter, r *http.Request)
json.NewEncoder(w).Encode(LogoutResponseData{Status: "success"})
}
// LoginHandler creates a new refresh & access token for the user if given the correct credentials
func (h *TaskcafeHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
var requestData LoginRequestData
err := json.NewDecoder(r.Body).Decode(&requestData)
@ -171,8 +180,8 @@ func (h *TaskcafeHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(requestData.Password))
if err != nil {
log.WithFields(log.Fields{
"username": requestData.Username,
}).Warn("password incorrect for user")
"username": requestData.Username,
}).Warn("password incorrect for user")
w.WriteHeader(http.StatusUnauthorized)
return
}
@ -196,6 +205,7 @@ func (h *TaskcafeHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(LoginResponseData{accessTokenString, false})
}
// InstallHandler creates first user on fresh install
func (h *TaskcafeHandler) InstallHandler(w http.ResponseWriter, r *http.Request) {
if restricted, ok := r.Context().Value("restricted_mode").(auth.RestrictedMode); ok {
if restricted != auth.InstallOnly {
@ -256,6 +266,7 @@ func (h *TaskcafeHandler) InstallHandler(w http.ResponseWriter, r *http.Request)
json.NewEncoder(w).Encode(LoginResponseData{accessTokenString, false})
}
// Routes registers all authentication routes
func (rs authResource) Routes(taskcafeHandler TaskcafeHandler) chi.Router {
r := chi.NewRouter()
r.Post("/login", taskcafeHandler.LoginHandler)

View File

@ -16,6 +16,7 @@ import (
"github.com/jordanknott/taskcafe/internal/frontend"
)
// Frontend serves the index.html file
func (h *TaskcafeHandler) Frontend(w http.ResponseWriter, r *http.Request) {
f, err := frontend.Frontend.Open("index.h")
if os.IsNotExist(err) {
@ -26,6 +27,7 @@ func (h *TaskcafeHandler) Frontend(w http.ResponseWriter, r *http.Request) {
http.ServeContent(w, r, "index.html", time.Now(), f)
}
// ProfileImageUpload handles a user uploading a new avatar profile image
func (h *TaskcafeHandler) ProfileImageUpload(w http.ResponseWriter, r *http.Request) {
log.Info("preparing to upload file")
userID, ok := r.Context().Value("userID").(uuid.UUID)

View File

@ -10,6 +10,19 @@ import (
log "github.com/sirupsen/logrus"
)
// ContextKey represents a context key
type ContextKey string
const (
// UserIDKey is the key for the user id of the authenticated user
UserIDKey ContextKey = "userID"
//RestrictedModeKey is the key for whether the authenticated user only has access to install route
RestrictedModeKey ContextKey = "restricted_mode"
// OrgRoleKey is the key for the organization role code of the authenticated user
OrgRoleKey ContextKey = "org_role"
)
// AuthenticationMiddleware is a middleware that requires a valid JWT token to be passed via the Authorization header
func AuthenticationMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
bearerTokenRaw := r.Header.Get("Authorization")
@ -51,9 +64,9 @@ func AuthenticationMiddleware(next http.Handler) http.Handler {
return
}
}
ctx := context.WithValue(r.Context(), "userID", userID)
ctx = context.WithValue(ctx, "restricted_mode", accessClaims.Restricted)
ctx = context.WithValue(ctx, "org_role", accessClaims.OrgRole)
ctx := context.WithValue(r.Context(), UserIDKey, userID)
ctx = context.WithValue(ctx, RestrictedModeKey, accessClaims.Restricted)
ctx = context.WithValue(ctx, OrgRoleKey, accessClaims.OrgRole)
next.ServeHTTP(w, r.WithContext(ctx))
})

View File

@ -10,23 +10,22 @@ import (
"github.com/jmoiron/sqlx"
log "github.com/sirupsen/logrus"
"os"
"path/filepath"
"github.com/jordanknott/taskcafe/internal/db"
"github.com/jordanknott/taskcafe/internal/frontend"
"github.com/jordanknott/taskcafe/internal/graph"
"github.com/jordanknott/taskcafe/internal/logger"
"os"
"path/filepath"
)
// spaHandler implements the http.Handler interface, so we can use it
// to respond to HTTP requests. The path to the static directory and
// path to the index file within that static directory are used to
// serve the SPA in the given static directory.
// FrontendHandler serves an embed React client through chi
type FrontendHandler struct {
staticPath string
indexPath string
}
// IsDir checks if the given file is a directory
func IsDir(f http.File) bool {
fi, err := f.Stat()
if err != nil {
@ -35,6 +34,7 @@ func IsDir(f http.File) bool {
return fi.IsDir()
}
// ServeHTTP attempts to serve a requested file for the embedded React client
func (h FrontendHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path, err := filepath.Abs(r.URL.Path)
if err != nil {
@ -58,10 +58,12 @@ func (h FrontendHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.ServeContent(w, r, path, time.Now(), f)
}
// TaskcafeHandler contains all the route handlers
type TaskcafeHandler struct {
repo db.Repository
}
// NewRouter creates a new router for chi
func NewRouter(dbConnection *sqlx.DB) (chi.Router, error) {
formatter := new(log.TextFormatter)
formatter.TimestampFormat = "02-01-2006 15:04:05"