Start refactoring
This commit is contained in:
215
server/admin.go
215
server/admin.go
@@ -1,220 +1,33 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"opensavecloudserver/admin"
|
||||
"opensavecloudserver/authentication"
|
||||
"opensavecloudserver/database"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UpdateUsername struct {
|
||||
Id int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
func (s *HTTPServer) createUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func AddUser(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
userInfo := new(authentication.Registration)
|
||||
err = json.Unmarshal(body, userInfo)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = authentication.Register(userInfo)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
user, err := database.UserByUsername(userInfo.Username)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(user, w, r)
|
||||
func (s *HTTPServer) deleteUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func RemoveUser(w http.ResponseWriter, r *http.Request) {
|
||||
queryId := chi.URLParam(r, "id")
|
||||
id, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
user, err := database.UserById(id)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = admin.RemoveUser(user)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(user, w, r)
|
||||
func (s *HTTPServer) listAllServerUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func AllUsers(w http.ResponseWriter, r *http.Request) {
|
||||
users, err := database.AllUsers()
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(users, w, r)
|
||||
func (s *HTTPServer) userHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func User(w http.ResponseWriter, r *http.Request) {
|
||||
queryId := chi.URLParam(r, "id")
|
||||
id, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
user, err := database.UserById(id)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(user, w, r)
|
||||
func (s *HTTPServer) updateUserRoleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func SetAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
queryId := chi.URLParam(r, "id")
|
||||
id, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
user, err := database.UserById(id)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = admin.SetAdmin(user)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(user, w, r)
|
||||
func (s *HTTPServer) updateUserPasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func SetNotAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
queryId := chi.URLParam(r, "id")
|
||||
id, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
user, err := database.UserById(id)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = admin.RemoveAdminRole(user)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(user, w, r)
|
||||
}
|
||||
|
||||
func ChangeUserPassword(w http.ResponseWriter, r *http.Request) {
|
||||
queryId := chi.URLParam(r, "id")
|
||||
userId, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
newPassword := new(NewPassword)
|
||||
err = json.Unmarshal(body, newPassword)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if newPassword.Password != newPassword.VerifyPassword {
|
||||
badRequest("password are not the same", w, r)
|
||||
return
|
||||
}
|
||||
err = database.ChangePassword(userId, []byte(newPassword.Password))
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := &successMessage{
|
||||
Message: "Password changed",
|
||||
Timestamp: time.Now(),
|
||||
Status: 200,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
}
|
||||
|
||||
func ChangeUsername(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
newUserInfo := new(UpdateUsername)
|
||||
err = json.Unmarshal(body, newUserInfo)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if len(newUserInfo.Username) < 3 {
|
||||
badRequest("username need at least 3 characters", w, r)
|
||||
return
|
||||
}
|
||||
_, err = database.UserByUsername(newUserInfo.Username)
|
||||
if err == nil {
|
||||
badRequest("username already exist", w, r)
|
||||
return
|
||||
}
|
||||
err = database.ChangeUsername(newUserInfo.Id, newUserInfo.Username)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := &successMessage{
|
||||
Message: "Username changed",
|
||||
Timestamp: time.Now(),
|
||||
Status: 200,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
func (s *HTTPServer) updateUsernameHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@@ -3,94 +3,88 @@ package server
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"opensavecloudserver/authentication"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
type (
|
||||
userLogin struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
userRegistration struct {
|
||||
UserUsername string `json:"username"`
|
||||
UserPassword string `json:"password"`
|
||||
UserDisplayName string `json:"displayName"`
|
||||
}
|
||||
|
||||
userPresenter struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
DisplayName string `json:"displayName"`
|
||||
}
|
||||
|
||||
jwtPresenter struct {
|
||||
Token []byte `json:"token"`
|
||||
}
|
||||
)
|
||||
|
||||
func (ur userRegistration) Username() string {
|
||||
return ur.UserUsername
|
||||
}
|
||||
|
||||
type TokenValidation struct {
|
||||
Valid bool `json:"valid"`
|
||||
func (ur userRegistration) Password() string {
|
||||
return ur.UserPassword
|
||||
}
|
||||
|
||||
func Login(w http.ResponseWriter, r *http.Request) {
|
||||
func (ur userRegistration) DisplayName() string {
|
||||
return ur.UserDisplayName
|
||||
}
|
||||
|
||||
func (s *HTTPServer) loginUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
credential := new(Credential)
|
||||
err = json.Unmarshal(body, credential)
|
||||
|
||||
var ul userLogin
|
||||
err = json.Unmarshal(body, &ul)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
token, err := authentication.Connect(credential.Username, credential.Password)
|
||||
|
||||
jwt, err := s.deps.Authenticator.Authenticate(ul.Username, ul.Password)
|
||||
if err != nil {
|
||||
unauthorized(w, r)
|
||||
return
|
||||
}
|
||||
ok(token, w, r)
|
||||
|
||||
payload := jwtPresenter{
|
||||
Token: jwt,
|
||||
}
|
||||
ok(payload, w)
|
||||
}
|
||||
|
||||
func Register(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *HTTPServer) registerUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
func CheckToken(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
var ur userRegistration
|
||||
err = json.Unmarshal(body, &ur)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
credential := new(authentication.AccessToken)
|
||||
err = json.Unmarshal(body, credential)
|
||||
|
||||
u, err := s.deps.UserRepository.CreateUser(ur)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
_, err = authentication.ParseToken(credential.Token)
|
||||
if err != nil {
|
||||
payload := TokenValidation{
|
||||
Valid: false,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
return
|
||||
payload := userPresenter{
|
||||
ID: u.ID().String(),
|
||||
Username: u.Username(),
|
||||
DisplayName: u.DisplayName(),
|
||||
}
|
||||
payload := TokenValidation{
|
||||
Valid: true,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
ok(payload, w)
|
||||
}
|
||||
|
||||
25
server/authentication/authentication.go
Normal file
25
server/authentication/authentication.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package authentication
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"opensavecloudserver/data/repository/user"
|
||||
)
|
||||
|
||||
type (
|
||||
Authenticator interface {
|
||||
Authenticate(username, password string) ([]byte, error)
|
||||
Validate(token string) (Session, error)
|
||||
}
|
||||
|
||||
Session interface {
|
||||
UserID() user.ID
|
||||
Scopes() []Scope
|
||||
Roles() []user.Role
|
||||
}
|
||||
|
||||
Scope string
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadPassword = errors.New("failed to verify password")
|
||||
)
|
||||
128
server/authentication/impl/impl.go
Normal file
128
server/authentication/impl/impl.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package impl
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"github.com/lestrrat-go/jwx/v2/jwa"
|
||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||
"github.com/lestrrat-go/jwx/v2/jwt"
|
||||
"opensavecloudserver/data/repository/user"
|
||||
"opensavecloudserver/server/authentication"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
JWTAuthenticator struct {
|
||||
userRepo user.UserRepository
|
||||
key jwk.Key
|
||||
pubKey jwk.Key
|
||||
}
|
||||
|
||||
JWTSession struct {
|
||||
id user.ID
|
||||
scopes []authentication.Scope
|
||||
roles []user.Role
|
||||
}
|
||||
|
||||
claimKey string
|
||||
)
|
||||
|
||||
func (J JWTSession) UserID() user.ID {
|
||||
return J.id
|
||||
}
|
||||
|
||||
func (J JWTSession) Scopes() []authentication.Scope {
|
||||
return J.scopes
|
||||
}
|
||||
|
||||
func (J JWTSession) Roles() []user.Role {
|
||||
return J.roles
|
||||
}
|
||||
|
||||
const (
|
||||
userScopesClaimKey claimKey = "user.scopes"
|
||||
userRolesClaimKey claimKey = "user.roles"
|
||||
|
||||
issuerName string = "oscs"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrScopesNotFound = errors.New("scopes not found in JWT")
|
||||
ErrRolesNotFound = errors.New("roles not found in JWT")
|
||||
)
|
||||
|
||||
func (J *JWTAuthenticator) Authenticate(username, password string) ([]byte, error) {
|
||||
u, err := J.userRepo.UserByUsername(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.CheckPassword(password) {
|
||||
// Build a JWT!
|
||||
tok, err := jwt.NewBuilder().
|
||||
Issuer(issuerName).
|
||||
IssuedAt(time.Now()).
|
||||
Subject(u.ID().String()).
|
||||
Claim(string(userScopesClaimKey), "").
|
||||
Claim(string(userRolesClaimKey), u.Roles()).
|
||||
Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sign a JWT!
|
||||
return jwt.Sign(tok, jwt.WithKey(jwa.RS256, J.key))
|
||||
}
|
||||
return nil, authentication.ErrBadPassword
|
||||
}
|
||||
|
||||
func (J *JWTAuthenticator) Validate(token string) (authentication.Session, error) {
|
||||
verifiedToken, err := jwt.Parse([]byte(token), jwt.WithKey(jwa.RS256, J.pubKey), jwt.WithValidate(true), jwt.WithIssuer(issuerName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, err := uuid.Parse(verifiedToken.Subject())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, ok := verifiedToken.Get(string(userScopesClaimKey))
|
||||
if !ok {
|
||||
return nil, ErrScopesNotFound
|
||||
}
|
||||
scopes, ok := value.([]authentication.Scope)
|
||||
if !ok {
|
||||
return nil, ErrScopesNotFound
|
||||
}
|
||||
value, ok = verifiedToken.Get(string(userRolesClaimKey))
|
||||
if !ok {
|
||||
return nil, ErrRolesNotFound
|
||||
}
|
||||
roles, ok := value.([]user.Role)
|
||||
if !ok {
|
||||
return nil, ErrRolesNotFound
|
||||
}
|
||||
return JWTSession{
|
||||
id: user.ID(id),
|
||||
scopes: scopes,
|
||||
roles: roles,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewJWTAuthenticator(key *rsa.PrivateKey, userRepo user.UserRepository) (authentication.Authenticator, error) {
|
||||
a := new(JWTAuthenticator)
|
||||
a.userRepo = userRepo
|
||||
// Parse, serialize, slice and dice JWKs!
|
||||
privkey, err := jwk.FromRaw(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubkey, err := jwk.PublicKeyOf(privkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.key = privkey
|
||||
a.pubKey = pubkey
|
||||
return a, nil
|
||||
}
|
||||
348
server/data.go
348
server/data.go
@@ -1,348 +1,42 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"io"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"opensavecloudserver/config"
|
||||
"opensavecloudserver/database"
|
||||
"opensavecloudserver/upload"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type NewGameInfo struct {
|
||||
Name string `json:"name"`
|
||||
// createSaveEntryHandler create a game entry to the database
|
||||
func (s *HTTPServer) createSaveEntryHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
type UploadGameInfo struct {
|
||||
GameId int `json:"game_id"`
|
||||
// saveInformationHandler get the game save information from the database
|
||||
func (s *HTTPServer) saveInformationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
type LockError struct {
|
||||
Message string `json:"message"`
|
||||
// allUserSavesInformationHandler all game saves information for a user
|
||||
func (s *HTTPServer) allUserSavesInformationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
type NewPassword struct {
|
||||
Password string `json:"password"`
|
||||
VerifyPassword string `json:"verify_password"`
|
||||
// uploadDataHandler upload the game save archive to the storage folder
|
||||
func (s *HTTPServer) uploadDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// CreateGame create a game entry to the database
|
||||
func CreateGame(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
gameInfo := new(NewGameInfo)
|
||||
err = json.Unmarshal(body, gameInfo)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
game, err := database.CreateGame(userId, gameInfo.Name)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(game, w, r)
|
||||
// downloadDataHandler send the game save archive to the client
|
||||
func (s *HTTPServer) downloadDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// GameInfoByID get the game save information from the database
|
||||
func GameInfoByID(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
queryId := chi.URLParam(r, "id")
|
||||
id, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
badRequest("Game ID missing or not an int", w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
game, err := database.GameInfoById(userId, id)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(game, w, r)
|
||||
func (s *HTTPServer) currentUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// AllGamesInformation all game saves information for a user
|
||||
func AllGamesInformation(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
games, err := database.GameInfosByUserId(userId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(games, w, r)
|
||||
func (s *HTTPServer) updateCurrentUserPasswordHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// AskForUpload check if the game save is not lock, then lock it and generate a token
|
||||
func AskForUpload(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
gameInfo := new(UploadGameInfo)
|
||||
err = json.Unmarshal(body, gameInfo)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
token, err := upload.AskForUpload(userId, gameInfo.GameId)
|
||||
if err != nil {
|
||||
ok(LockError{Message: err.Error()}, w, r)
|
||||
return
|
||||
}
|
||||
ok(token, w, r)
|
||||
}
|
||||
|
||||
// UploadSave upload the game save archive to the storage folder
|
||||
func UploadSave(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
gameId, err := gameIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer upload.UnlockGame(gameId)
|
||||
hash := r.Header.Get("X-Game-Save-Hash")
|
||||
if utf8.RuneCountInString(hash) == 0 {
|
||||
badRequest("The header X-Game-Save-Hash is missing", w, r)
|
||||
return
|
||||
}
|
||||
archiveHash := strings.ToLower(r.Header.Get("X-Hash"))
|
||||
if utf8.RuneCountInString(hash) == 0 {
|
||||
badRequest("The header X-Hash is missing", w, r)
|
||||
return
|
||||
}
|
||||
game, err := database.GameInfoById(userId, gameId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
file, _, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer func(file multipart.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}(file)
|
||||
err = upload.UploadToCache(file, game)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = upload.ValidateAndMove(game, archiveHash)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = database.UpdateGameRevision(game, hash)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := &successMessage{
|
||||
Message: "Game uploaded",
|
||||
Timestamp: time.Now(),
|
||||
Status: 200,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
}
|
||||
|
||||
// Download send the game save archive to the client
|
||||
func Download(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
gameId, err := gameIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer upload.UnlockGame(gameId)
|
||||
game, err := database.GameInfoById(userId, gameId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
savePath := filepath.Join(config.Path().Storage, strconv.Itoa(userId), game.PathStorage)
|
||||
|
||||
if _, err := os.Stat(savePath); err == nil {
|
||||
hash, err := upload.FileHash(savePath)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
file, err := os.Open(savePath)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}(file)
|
||||
w.Header().Add("X-Hash", strings.ToUpper(hash))
|
||||
_, err = io.Copy(w, file)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func UserInformation(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
user, err := database.UserById(userId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(user, w, r)
|
||||
}
|
||||
|
||||
func ChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
newPassword := new(NewPassword)
|
||||
err = json.Unmarshal(body, newPassword)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if newPassword.Password != newPassword.VerifyPassword {
|
||||
badRequest("password are not the same", w, r)
|
||||
return
|
||||
}
|
||||
err = database.ChangePassword(userId, []byte(newPassword.Password))
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
payload := &successMessage{
|
||||
Message: "Password changed",
|
||||
Timestamp: time.Now(),
|
||||
Status: 200,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
}
|
||||
|
||||
func RemoveGame(w http.ResponseWriter, r *http.Request) {
|
||||
userId, err := userIdFromContext(r.Context())
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
queryId := chi.URLParam(r, "id")
|
||||
id, err := strconv.Atoi(queryId)
|
||||
if err != nil {
|
||||
badRequest("Game ID missing or not an int", w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
game, err := database.GameInfoById(userId, id)
|
||||
if err != nil {
|
||||
notFound(err.Error(), w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = upload.RemoveGame(userId, game)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = database.RemoveGame(game)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
ok(game, w, r)
|
||||
func (s *HTTPServer) deleteSave(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
36
server/middleware.go
Normal file
36
server/middleware.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package server
|
||||
|
||||
import "net/http"
|
||||
|
||||
// authMiddleware check the authentication token before accessing to the resource
|
||||
func (s *HTTPServer) authMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
})
|
||||
}
|
||||
|
||||
// adminMiddleware check the role of the user before accessing to the resource
|
||||
func (s *HTTPServer) adminMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
})
|
||||
}
|
||||
|
||||
// uploadMiddleware check the upload key before allowing to upload a file
|
||||
func (s *HTTPServer) uploadMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO
|
||||
})
|
||||
}
|
||||
|
||||
func recoverMiddleware(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)
|
||||
})
|
||||
}
|
||||
@@ -15,15 +15,9 @@ type httpError struct {
|
||||
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,
|
||||
Status: http.StatusInternalServerError,
|
||||
Error: "Internal Server Error",
|
||||
Message: "The server encountered an unexpected condition that prevented it from fulfilling the request.",
|
||||
Path: r.RequestURI,
|
||||
@@ -35,7 +29,7 @@ func internalServerError(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -44,7 +38,7 @@ func internalServerError(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func notFound(message string, w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 404,
|
||||
Status: http.StatusNotFound,
|
||||
Error: "Not Found",
|
||||
Message: message,
|
||||
Path: r.RequestURI,
|
||||
@@ -56,7 +50,28 @@ func notFound(message string, w http.ResponseWriter, r *http.Request) {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(404)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: http.StatusMethodNotAllowed,
|
||||
Error: "Method Not Allowed",
|
||||
Message: "The server knows the request method, but the target resource doesn't support this method",
|
||||
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(http.StatusMethodNotAllowed)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -65,7 +80,7 @@ func notFound(message string, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func unauthorized(w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 401,
|
||||
Status: http.StatusUnauthorized,
|
||||
Error: "Unauthorized",
|
||||
Message: "The request has not been completed because it lacks valid authentication credentials for the requested resource.",
|
||||
Path: r.RequestURI,
|
||||
@@ -77,8 +92,8 @@ func unauthorized(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Header().Add("WWW-Authenticate", "Custom realm=\"Login via /api/login\"")
|
||||
w.WriteHeader(401)
|
||||
w.Header().Add("WWW-Authenticate", "Custom realm=\"loginUserHandler via /api/login\"")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -87,8 +102,8 @@ func unauthorized(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func forbidden(w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 403,
|
||||
Error: "Unauthorized",
|
||||
Status: http.StatusForbidden,
|
||||
Error: "Forbidden",
|
||||
Message: "The access is permanently forbidden and tied to the application logic, such as insufficient rights to a resource.",
|
||||
Path: r.RequestURI,
|
||||
Timestamp: time.Now(),
|
||||
@@ -99,14 +114,14 @@ func forbidden(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(403)
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ok(obj interface{}, w http.ResponseWriter, _ *http.Request) {
|
||||
func ok(obj interface{}, w http.ResponseWriter) {
|
||||
payload, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
@@ -120,7 +135,7 @@ func ok(obj interface{}, w http.ResponseWriter, _ *http.Request) {
|
||||
|
||||
func badRequest(message string, w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 400,
|
||||
Status: http.StatusBadRequest,
|
||||
Error: "Bad Request",
|
||||
Message: message,
|
||||
Path: r.RequestURI,
|
||||
@@ -132,7 +147,7 @@ func badRequest(message string, w http.ResponseWriter, r *http.Request) {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
|
||||
246
server/server.go
246
server/server.go
@@ -1,166 +1,138 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"log"
|
||||
"net/http"
|
||||
"opensavecloudserver/authentication"
|
||||
"opensavecloudserver/config"
|
||||
"opensavecloudserver/database"
|
||||
"opensavecloudserver/upload"
|
||||
"opensavecloudserver/data/repository/game"
|
||||
"opensavecloudserver/data/repository/user"
|
||||
"opensavecloudserver/server/authentication"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ContextKey string
|
||||
type (
|
||||
HTTPServer struct {
|
||||
Server *http.Server
|
||||
config config.Configuration
|
||||
deps DatasourceDependencies
|
||||
}
|
||||
|
||||
const (
|
||||
UserIdKey ContextKey = "userId"
|
||||
GameIdKey ContextKey = "gameId"
|
||||
DatasourceDependencies struct {
|
||||
UserRepository user.UserRepository
|
||||
GameRepository game.GameRepository
|
||||
Authenticator authentication.Authenticator
|
||||
}
|
||||
)
|
||||
|
||||
// Serve start the http server
|
||||
func Serve() {
|
||||
// NewServer start the http server
|
||||
func NewServer(config config.Configuration, deps DatasourceDependencies) *HTTPServer {
|
||||
s := &HTTPServer{config: config, deps: deps}
|
||||
router := chi.NewRouter()
|
||||
router.NotFound(func(writer http.ResponseWriter, request *http.Request) {
|
||||
notFound("This route does not exist", writer, request)
|
||||
})
|
||||
router.MethodNotAllowed(func(writer http.ResponseWriter, request *http.Request) {
|
||||
methodNotAllowed(writer, request)
|
||||
})
|
||||
router.Use(middleware.Logger)
|
||||
router.Use(recovery)
|
||||
router.Route("/api", func(rApi chi.Router) {
|
||||
rApi.Route("/v1", func(r chi.Router) {
|
||||
r.Post("/login", Login)
|
||||
r.Post("/check/token", CheckToken)
|
||||
if config.Features().AllowRegister {
|
||||
r.Post("/register", Register)
|
||||
router.Use(recoverMiddleware)
|
||||
if config.Server.Compress {
|
||||
router.Use(middleware.Compress(5, "application/json", "application/octet‑stream"))
|
||||
}
|
||||
if config.Server.PingEndPoint {
|
||||
router.Use(middleware.Heartbeat("/heartbeat"))
|
||||
}
|
||||
router.Route("/api", func(routerAPI chi.Router) {
|
||||
routerAPI.Route("/v1", func(r chi.Router) {
|
||||
// Unsupported V1 because it was shitty, sorry about that
|
||||
r.HandleFunc("/*", unsupportedAPIHandler)
|
||||
})
|
||||
routerAPI.Route("/v2", func(r chi.Router) {
|
||||
// Get information about the server
|
||||
r.Get("/version", s.Information)
|
||||
// Authentication routes
|
||||
// Login and get a token
|
||||
r.Post("/login", s.loginUserHandler)
|
||||
if config.Features.AllowRegister {
|
||||
// Register a user
|
||||
r.Post("/register", s.registerUserHandler)
|
||||
}
|
||||
r.Route("/system", func(systemRouter chi.Router) {
|
||||
systemRouter.Get("/information", Information)
|
||||
})
|
||||
r.Route("/admin", func(adminRouter chi.Router) {
|
||||
adminRouter.Use(adminMiddleware)
|
||||
adminRouter.Post("/user", AddUser)
|
||||
adminRouter.Post("/user/username", ChangeUsername)
|
||||
adminRouter.Post("/user/passwd/{id}", ChangeUserPassword)
|
||||
adminRouter.Delete("/user/{id}", RemoveUser)
|
||||
adminRouter.Get("/user/{id}", User)
|
||||
adminRouter.Get("/users", AllUsers)
|
||||
adminRouter.Get("/user/role/admin/{id}", SetAdmin)
|
||||
adminRouter.Get("/user/role/user/{id}", SetNotAdmin)
|
||||
})
|
||||
// Secured routes
|
||||
r.Group(func(secureRouter chi.Router) {
|
||||
secureRouter.Use(authMiddleware)
|
||||
secureRouter.Use(s.authMiddleware)
|
||||
// Logged user routes
|
||||
secureRouter.Route("/user", func(userRouter chi.Router) {
|
||||
userRouter.Get("/information", UserInformation)
|
||||
userRouter.Post("/passwd", ChangePassword)
|
||||
// Get information about the logged user
|
||||
userRouter.Get("/", s.currentUserHandler)
|
||||
// Change the password of the current user
|
||||
userRouter.Put("/password/update", s.updateCurrentUserPasswordHandler)
|
||||
})
|
||||
secureRouter.Route("/game", func(gameRouter chi.Router) {
|
||||
gameRouter.Post("/create", CreateGame)
|
||||
gameRouter.Get("/all", AllGamesInformation)
|
||||
gameRouter.Delete("/remove/{id}", RemoveGame)
|
||||
gameRouter.Get("/info/{id}", GameInfoByID)
|
||||
gameRouter.Post("/upload/init", AskForUpload)
|
||||
// Save files routes
|
||||
secureRouter.Route("/saves", func(gameRouter chi.Router) {
|
||||
// Create a save entry
|
||||
gameRouter.Post("/create", s.createSaveEntryHandler)
|
||||
// List all available saves
|
||||
gameRouter.Get("/", s.allUserSavesInformationHandler)
|
||||
// Remove a save
|
||||
gameRouter.Delete("/{id}", s.deleteSave)
|
||||
// Get the information about a save
|
||||
gameRouter.Get("/{id}", s.saveInformationHandler)
|
||||
// Data routes
|
||||
gameRouter.Group(func(uploadRouter chi.Router) {
|
||||
uploadRouter.Use(uploadMiddleware)
|
||||
uploadRouter.Post("/upload", UploadSave)
|
||||
uploadRouter.Get("/download", Download)
|
||||
uploadRouter.Use(s.uploadMiddleware)
|
||||
// Upload data
|
||||
uploadRouter.Put("/{id}/data", s.uploadDataHandler)
|
||||
// downloadDataHandler data
|
||||
uploadRouter.Get("/{id}/data", s.downloadDataHandler)
|
||||
})
|
||||
})
|
||||
secureRouter.Route("/admin", func(adminRouter chi.Router) {
|
||||
adminRouter.Use(s.adminMiddleware)
|
||||
// Create a user
|
||||
adminRouter.Post("/user/create", s.createUserHandler)
|
||||
// Update the username of a user
|
||||
adminRouter.Post("/user/{id}/username", s.updateUsernameHandler)
|
||||
// Update the password of a user
|
||||
adminRouter.Post("/user/{id}/password/update", s.updateUserPasswordHandler)
|
||||
// Remove a user
|
||||
adminRouter.Delete("/user/{id}", s.deleteUserHandler)
|
||||
// Get information about a user
|
||||
adminRouter.Get("/user/{id}", s.userHandler)
|
||||
// List all user registered on the server
|
||||
adminRouter.Get("/users", s.listAllServerUsersHandler)
|
||||
// Update role
|
||||
adminRouter.Put("/user/{id}/role", s.updateUserRoleHandler)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
log.Println("Server is listening...")
|
||||
err := http.ListenAndServe(fmt.Sprintf(":%d", config.Server().Port), router)
|
||||
s.Server = &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", config.Server.Port),
|
||||
Handler: router,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func unsupportedAPIHandler(w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: http.StatusGone,
|
||||
Error: "API Not supported anymore",
|
||||
Message: "This version of the server does not support the V1 version of the API.",
|
||||
Path: r.RequestURI,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
payload, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusGone)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// authMiddleware check the authentication token before accessing to the resource
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
// adminMiddleware check the role of the user before accessing to the resource
|
||||
func adminMiddleware(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
|
||||
}
|
||||
user, err := database.UserById(userId)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if !user.IsAdmin {
|
||||
forbidden(w, r)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), UserIdKey, userId)
|
||||
r = r.WithContext(ctx)
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
unauthorized(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// uploadMiddleware check the upload key before allowing to upload a file
|
||||
func uploadMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
header := r.Header.Get("X-Upload-Key")
|
||||
if len(header) > 0 {
|
||||
if gameId, ok := upload.CheckUploadToken(header); ok {
|
||||
ctx := context.WithValue(r.Context(), GameIdKey, gameId)
|
||||
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 gameIdFromContext(ctx context.Context) (int, error) {
|
||||
if gameId, ok := ctx.Value(GameIdKey).(int); ok {
|
||||
return gameId, nil
|
||||
}
|
||||
return 0, errors.New("gameId 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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"opensavecloudserver/config"
|
||||
"opensavecloudserver/constant"
|
||||
"runtime"
|
||||
)
|
||||
@@ -10,20 +9,20 @@ import (
|
||||
type information struct {
|
||||
AllowRegister bool `json:"allow_register"`
|
||||
Version string `json:"version"`
|
||||
ApiVersion int `json:"api_version"`
|
||||
APIVersion int `json:"api_version"`
|
||||
GoVersion string `json:"go_version"`
|
||||
OsName string `json:"os_name"`
|
||||
OsArchitecture string `json:"os_architecture"`
|
||||
OSName string `json:"os_name"`
|
||||
OSArchitecture string `json:"os_architecture"`
|
||||
}
|
||||
|
||||
func Information(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *HTTPServer) Information(w http.ResponseWriter, r *http.Request) {
|
||||
info := information{
|
||||
AllowRegister: config.Features().AllowRegister,
|
||||
AllowRegister: s.config.Features.AllowRegister,
|
||||
Version: constant.Version,
|
||||
ApiVersion: constant.ApiVersion,
|
||||
APIVersion: constant.ApiVersion,
|
||||
GoVersion: runtime.Version(),
|
||||
OsName: runtime.GOOS,
|
||||
OsArchitecture: runtime.GOARCH,
|
||||
OSName: runtime.GOOS,
|
||||
OSArchitecture: runtime.GOARCH,
|
||||
}
|
||||
ok(info, w, r)
|
||||
ok(info, w)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user