diff options
Diffstat (limited to 'server/admin/handler.go')
-rw-r--r-- | server/admin/handler.go | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/server/admin/handler.go b/server/admin/handler.go new file mode 100644 index 0000000..5fd1fee --- /dev/null +++ b/server/admin/handler.go @@ -0,0 +1,119 @@ +package admin + +import ( + "encoding/json" + "net/http" + "os" + "time" + + "git.huntm.net/wedding/server/guest" + "github.com/golang-jwt/jwt/v5" +) + +type AdminHandler struct { + adminStore adminStore + guestStore guest.GuestStore +} + +type adminStore interface { + Find(admin Admin) (Admin, error) +} + +type appError struct { + Error error + Message string + Code int +} + +func NewAdminHandler(adminStore adminStore, guestStore guest.GuestStore) *AdminHandler { + return &AdminHandler{adminStore, guestStore} +} + +func (adminHandler *AdminHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) { + switch { + case request.Method == http.MethodOptions: + responseWriter.WriteHeader(http.StatusOK) + case request.Method == http.MethodPost && request.URL.Path == "/admin/login": + adminHandler.handleLogIn(responseWriter, request) + default: + responseWriter.WriteHeader(http.StatusNotFound) + } +} + +func (adminHandler *AdminHandler) handleLogIn(responseWriter http.ResponseWriter, request *http.Request) { + token, err := adminHandler.logIn(request) + if err != nil { + http.Error(responseWriter, err.Message, err.Code) + } else { + responseWriter.Write(token) + } +} + +func (adminHandler *AdminHandler) logIn(request *http.Request) ([]byte, *appError) { + requestAdmin, err := adminHandler.decodeCredentials(request) + if err != nil { + return []byte{}, &appError{err, "failed to unmarshal request", http.StatusBadRequest} + } + _, err = adminHandler.adminStore.Find(requestAdmin) + if err != nil { + return []byte{}, &appError{err, "admin not found", http.StatusUnauthorized} + } + expirationTime := adminHandler.setExpirationTime() + claims := adminHandler.createClaims(requestAdmin, expirationTime) + key, err := adminHandler.readKey() + if err != nil { + return []byte{}, &appError{err, "failed to read secret key", http.StatusInternalServerError} + } + token, err := adminHandler.createToken(claims, key) + if err != nil { + return []byte{}, &appError{err, "failed to create token", http.StatusInternalServerError} + } + guests, err := adminHandler.guestStore.Get() + if err != nil { + return []byte{}, &appError{err, "failed to get guests", http.StatusInternalServerError} + } + jsonBytes, err := adminHandler.marshalResponse(guests, token) + if err != nil { + return []byte{}, &appError{err, "failed to marshal response", http.StatusInternalServerError} + } + return jsonBytes, nil +} + +func (adminHandler *AdminHandler) decodeCredentials(request *http.Request) (Admin, error) { + var admin Admin + err := json.NewDecoder(request.Body).Decode(&admin) + defer request.Body.Close() + return admin, err +} + +func (adminHandler *AdminHandler) setExpirationTime() time.Time { + return time.Now().Add(15 * time.Minute) +} + +func (adminHandler *AdminHandler) createClaims(admin Admin, expirationTime time.Time) *Claims { + return &Claims{ + admin, + jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(expirationTime), + }, + } +} + +func (adminHandler *AdminHandler) readKey() ([]byte, error) { + // TODO: use properties file + return os.ReadFile("C:\\Users\\mhunt\\admin.pem") +} + +func (adminHandler *AdminHandler) createToken(claims *Claims, key []byte) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(key) +} + +func (adminHandler *AdminHandler) marshalResponse(guests []guest.Guest, token string) ([]byte, error) { + loginResponse := adminHandler.createLoginResponse(guests, token) + return json.Marshal(loginResponse) +} + +func (adminHandler *AdminHandler) createLoginResponse(guests []guest.Guest, token string) *LoginResponse { + return &LoginResponse{guests, token} +} |