first commit

This commit is contained in:
Aurélie Delhaie
2022-05-08 14:08:27 +02:00
commit b20a53cc48
19 changed files with 611 additions and 0 deletions

64
server/authentication.go Normal file
View File

@@ -0,0 +1,64 @@
package server
import (
"encoding/json"
"io"
"log"
"net/http"
"opensavecloudserver/authentication"
"time"
)
type Credential struct {
Username string `json:"username"`
Password string `json:"password"`
}
func Login(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
internalServerError(w, r)
log.Println(err)
return
}
credential := new(Credential)
err = json.Unmarshal(body, credential)
if err != nil {
internalServerError(w, r)
log.Println(err)
return
}
token, err := authentication.Connect(credential.Username, credential.Password)
if err != nil {
unauthorized(w, r)
return
}
ok(token, w, r)
}
func Register(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
internalServerError(w, r)
log.Println(err)
return
}
registration := new(authentication.Registration)
err = json.Unmarshal(body, registration)
if err != nil {
internalServerError(w, r)
log.Println(err)
return
}
err = authentication.Register(registration)
if err != nil {
badRequest(err.Error(), w, r)
return
}
payload := successMessage{
Message: "You are now registered",
Timestamp: time.Now(),
Status: 200,
}
ok(payload, w, r)
}

1
server/data.go Normal file
View File

@@ -0,0 +1 @@
package server

98
server/response.go Normal file
View File

@@ -0,0 +1,98 @@
package server
import (
"encoding/json"
"log"
"net/http"
"time"
)
type httpError struct {
Status int `json:"status"`
Timestamp time.Time `json:"timestamp"`
Error string `json:"error"`
Message string `json:"message"`
Path string `json:"path"`
}
type successMessage struct {
Status int `json:"status"`
Timestamp time.Time `json:"timestamp"`
Message string `json:"message"`
}
func internalServerError(w http.ResponseWriter, r *http.Request) {
e := httpError{
Status: 500,
Error: "Internal Server Error",
Message: "The server encountered an unexpected condition that prevented it from fulfilling the request.",
Path: r.RequestURI,
Timestamp: time.Now(),
}
payload, err := json.Marshal(e)
if err != nil {
log.Println(err)
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(500)
_, err = w.Write(payload)
if err != nil {
log.Println(err)
}
}
func unauthorized(w http.ResponseWriter, r *http.Request) {
e := httpError{
Status: 401,
Error: "Unauthorized",
Message: "The request has not been completed because it lacks valid authentication credentials for the requested resource.",
Path: r.RequestURI,
Timestamp: time.Now(),
}
payload, err := json.Marshal(e)
if err != nil {
log.Println(err)
}
w.Header().Add("Content-Type", "application/json")
w.Header().Add("WWW-Authenticate", "Custom realm=\"Login via /api/login\"")
w.WriteHeader(401)
_, err = w.Write(payload)
if err != nil {
log.Println(err)
}
}
func ok(obj interface{}, w http.ResponseWriter, _ *http.Request) {
payload, err := json.Marshal(obj)
if err != nil {
log.Println(err)
}
w.Header().Add("Content-Type", "application/json")
_, err = w.Write(payload)
if err != nil {
log.Println(err)
}
}
func badRequest(message string, w http.ResponseWriter, r *http.Request) {
e := httpError{
Status: 400,
Error: "Bad Request",
Message: message,
Path: r.RequestURI,
Timestamp: time.Now(),
}
payload, err := json.Marshal(e)
if err != nil {
log.Println(err)
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(400)
_, err = w.Write(payload)
if err != nil {
log.Println(err)
}
}

78
server/server.go Normal file
View File

@@ -0,0 +1,78 @@
package server
import (
"context"
"errors"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"log"
"net/http"
"opensavecloudserver/authentication"
"opensavecloudserver/config"
)
type ContextKey string
const UserIdKey ContextKey = "userId"
// Serve start the http server
func Serve() {
router := chi.NewRouter()
router.Use(middleware.Logger)
router.Use(recovery)
router.Route("/api", func(r chi.Router) {
r.Post("/login", Login)
if config.Features().AllowRegister {
r.Post("/register", Register)
}
r.Route("/system", func(systemRouter chi.Router) {
systemRouter.Get("/information", Information)
})
r.Group(func(secureRouter chi.Router) {
secureRouter.Use(authMiddleware)
})
})
log.Println("Server is listening...")
err := http.ListenAndServe(":8080", router)
if err != nil {
log.Fatal(err)
}
}
// authMiddleware filter the request
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
header := r.Header.Get("Authorization")
if len(header) > 7 {
userId, err := authentication.ParseToken(header[7:])
if err != nil {
unauthorized(w, r)
return
}
ctx := context.WithValue(r.Context(), UserIdKey, userId)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
return
}
unauthorized(w, r)
})
}
func userIdFromContext(ctx context.Context) (int, error) {
if userId, ok := ctx.Value(UserIdKey).(int); ok {
return userId, nil
}
return 0, errors.New("userId not found in context")
}
func recovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
err := recover()
if err != nil {
internalServerError(w, r)
}
}()
next.ServeHTTP(w, r)
})
}

29
server/system.go Normal file
View File

@@ -0,0 +1,29 @@
package server
import (
"net/http"
"opensavecloudserver/config"
"opensavecloudserver/constant"
"runtime"
)
type information struct {
AllowRegister bool `json:"allow_register"`
Version string `json:"version"`
ApiVersion int `json:"api_version"`
GoVersion string `json:"go_version"`
OsName string `json:"os_name"`
OsArchitecture string `json:"os_architecture"`
}
func Information(w http.ResponseWriter, r *http.Request) {
info := information{
AllowRegister: config.Features().AllowRegister,
Version: constant.Version,
ApiVersion: constant.ApiVersion,
GoVersion: runtime.Version(),
OsName: runtime.GOOS,
OsArchitecture: runtime.GOARCH,
}
ok(info, w, r)
}