mirror of
https://github.com/notherealmarco/WASAPhoto.git
synced 2025-05-05 12:22:35 +02:00
Improve comments and code readability
This commit is contained in:
parent
f6ad6db2f7
commit
3de158e5a5
19 changed files with 84 additions and 43 deletions
|
@ -7,9 +7,11 @@ import (
|
|||
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||
)
|
||||
|
||||
// AnonymousAuth is the authentication provider for non logged-in users
|
||||
type AnonymousAuth struct {
|
||||
}
|
||||
|
||||
// Returns a newly created AnonymousAuth instance
|
||||
func BuildAnonymous() *AnonymousAuth {
|
||||
return &AnonymousAuth{}
|
||||
}
|
||||
|
@ -18,14 +20,17 @@ func (u *AnonymousAuth) GetType() string {
|
|||
return "Anonymous"
|
||||
}
|
||||
|
||||
// Returns UNAUTHORIZED, as anonymous users are logged in
|
||||
func (u *AnonymousAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
||||
return reqcontext.UNAUTHORIZED, nil
|
||||
}
|
||||
|
||||
// Returns UNAUTHORIZED, as anonymous users are not logged in
|
||||
func (u *AnonymousAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
||||
return reqcontext.UNAUTHORIZED, nil
|
||||
}
|
||||
|
||||
// Returns an empty string, as anonymous users have no user ID
|
||||
func (u *AnonymousAuth) GetUserID() string {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||
)
|
||||
|
||||
// BearerAuth is the authentication provider that authorizes users by Bearer tokens
|
||||
// In this case, a token is the unique identifier for a user.
|
||||
type BearerAuth struct {
|
||||
token string
|
||||
}
|
||||
|
@ -16,6 +18,8 @@ func (b *BearerAuth) GetType() string {
|
|||
return "Bearer"
|
||||
}
|
||||
|
||||
// Given the content of the Authorization header, returns a BearerAuth instance for the user
|
||||
// Returns an error if the header is not valid
|
||||
func BuildBearer(header string) (*BearerAuth, error) {
|
||||
if header == "" {
|
||||
return nil, errors.New("missing authorization header")
|
||||
|
@ -29,10 +33,12 @@ func BuildBearer(header string) (*BearerAuth, error) {
|
|||
return &BearerAuth{token: header[7:]}, nil
|
||||
}
|
||||
|
||||
// Returns the user ID of the user that is currently logged in
|
||||
func (b *BearerAuth) GetUserID() string {
|
||||
return b.token
|
||||
}
|
||||
|
||||
// Checks if the token is valid
|
||||
func (b *BearerAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
||||
// this is the way we manage authorization, the bearer token is the user id
|
||||
state, err := db.UserExists(b.token)
|
||||
|
@ -47,6 +53,7 @@ func (b *BearerAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus,
|
|||
return reqcontext.UNAUTHORIZED, nil
|
||||
}
|
||||
|
||||
// Checks if the given user and the currently logged in user are the same user
|
||||
func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
||||
|
||||
// If uid is not a valid user, return USER_NOT_FOUND
|
||||
|
@ -60,6 +67,7 @@ func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcon
|
|||
}
|
||||
|
||||
if b.token == uid {
|
||||
// If the user is the same as the one in the token, check if the user does actually exist in the database
|
||||
auth, err := b.Authorized(db)
|
||||
|
||||
if err != nil {
|
||||
|
@ -68,5 +76,6 @@ func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcon
|
|||
|
||||
return auth, nil
|
||||
}
|
||||
// If the user is not the same as the one in the token, return FORBIDDEN
|
||||
return reqcontext.FORBIDDEN, nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// BuildAuth returns an Authorization implementation for the currently logged in user
|
||||
func BuildAuth(header string) (reqcontext.Authorization, error) {
|
||||
auth, err := BuildBearer(header)
|
||||
if err != nil {
|
||||
|
@ -21,6 +22,8 @@ func BuildAuth(header string) (reqcontext.Authorization, error) {
|
|||
return auth, nil
|
||||
}
|
||||
|
||||
// Given a user authorization function, if the function returns some error, it sends the error to the client and return false
|
||||
// Otherwise it returns true without sending anything to the client
|
||||
func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error), uid string, db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger, notFoundStatus int) bool {
|
||||
auth, err := f(db, uid)
|
||||
if err != nil {
|
||||
|
@ -28,21 +31,25 @@ func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcont
|
|||
return false
|
||||
}
|
||||
if auth == reqcontext.UNAUTHORIZED {
|
||||
// The token is not valid
|
||||
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
||||
return false
|
||||
}
|
||||
if auth == reqcontext.FORBIDDEN {
|
||||
// The user is not authorized for this action
|
||||
helpers.SendStatus(http.StatusForbidden, w, "Forbidden", l)
|
||||
return false
|
||||
}
|
||||
// requested user is not found -> 404 as the resource is not found
|
||||
if auth == reqcontext.USER_NOT_FOUND {
|
||||
// Attempting to perform an action on a non-existent user
|
||||
helpers.SendStatus(notFoundStatus, w, "User not found", l)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Given a function that validates a token, if the function returns some error, it sends the error to the client and return false
|
||||
// Otherwise it returns true without sending anything to the client
|
||||
func SendErrorIfNotLoggedIn(f func(db database.AppDatabase) (reqcontext.AuthStatus, error), db database.AppDatabase, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||
|
||||
auth, err := f(db)
|
||||
|
@ -53,6 +60,7 @@ func SendErrorIfNotLoggedIn(f func(db database.AppDatabase) (reqcontext.AuthStat
|
|||
}
|
||||
|
||||
if auth == reqcontext.UNAUTHORIZED {
|
||||
// The token is not valid
|
||||
helpers.SendStatus(http.StatusUnauthorized, w, "Unauthorized", l)
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -66,6 +66,7 @@ func (rt *_router) PutBan(w http.ResponseWriter, r *http.Request, ps httprouter.
|
|||
return
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
status, err := rt.db.BanUser(uid, banned)
|
||||
|
||||
if err != nil {
|
||||
|
@ -95,6 +96,7 @@ func (rt *_router) DeleteBan(w http.ResponseWriter, r *http.Request, ps httprout
|
|||
return
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
status, err := rt.db.UnbanUser(uid, banned)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -55,6 +55,7 @@ func (rt *_router) GetComments(w http.ResponseWriter, r *http.Request, ps httpro
|
|||
}
|
||||
|
||||
// send the response
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(comments)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -79,6 +79,7 @@ func (rt *_router) PutFollow(w http.ResponseWriter, r *http.Request, ps httprout
|
|||
return
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
status, err := rt.db.FollowUser(follower, uid)
|
||||
|
||||
if err != nil {
|
||||
|
@ -109,6 +110,7 @@ func (rt *_router) DeleteFollow(w http.ResponseWriter, r *http.Request, ps httpr
|
|||
return
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
status, err := rt.db.UnfollowUser(follower, uid)
|
||||
|
||||
if err != nil {
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Tries to decode a json, if it fails, it returns Bad Request to the client and the function returns false
|
||||
// Otherwise it returns true without sending anything to the client
|
||||
func DecodeJsonOrBadRequest(r io.Reader, w http.ResponseWriter, v interface{}, l logrus.FieldLogger) bool {
|
||||
|
||||
err := json.NewDecoder(r).Decode(v)
|
||||
|
@ -20,6 +22,8 @@ func DecodeJsonOrBadRequest(r io.Reader, w http.ResponseWriter, v interface{}, l
|
|||
return true
|
||||
}
|
||||
|
||||
// Verifies if a user exists, if it doesn't, it returns Not Found to the client and the function returns false
|
||||
// Otherwise it returns true without sending anything to the client
|
||||
func VerifyUserOrNotFound(db database.AppDatabase, uid string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||
|
||||
user_exists, err := db.UserExists(uid)
|
||||
|
@ -36,6 +40,8 @@ func VerifyUserOrNotFound(db database.AppDatabase, uid string, w http.ResponseWr
|
|||
return true
|
||||
}
|
||||
|
||||
// Sends a generic status response
|
||||
// The response is a json object with a "status" field desribing the status of a request
|
||||
func SendStatus(httpStatus int, w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
||||
w.WriteHeader(httpStatus)
|
||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||
|
@ -44,40 +50,29 @@ func SendStatus(httpStatus int, w http.ResponseWriter, description string, l log
|
|||
}
|
||||
}
|
||||
|
||||
// Sends a Not Found error to the client
|
||||
func SendNotFound(w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||
if err != nil {
|
||||
l.WithError(err).Error("Error encoding json")
|
||||
}
|
||||
SendStatus(http.StatusNotFound, w, description, l)
|
||||
}
|
||||
|
||||
// Sends a Bad Request error to the client
|
||||
func SendBadRequest(w http.ResponseWriter, description string, l logrus.FieldLogger) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
err := json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||
if err != nil {
|
||||
l.WithError(err).Error("Error encoding json")
|
||||
}
|
||||
SendStatus(http.StatusBadRequest, w, description, l)
|
||||
}
|
||||
|
||||
// Sends a Bad Request error to the client and logs the given error
|
||||
func SendBadRequestError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
l.WithError(err).Error(description)
|
||||
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||
if err != nil {
|
||||
l.WithError(err).Error("Error encoding json")
|
||||
}
|
||||
SendBadRequest(w, description, l)
|
||||
}
|
||||
|
||||
// Sends an Internal Server Error to the client and logs the given error
|
||||
func SendInternalError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
l.WithError(err).Error(description)
|
||||
err = json.NewEncoder(w).Encode(structures.GenericResponse{Status: description})
|
||||
if err != nil {
|
||||
l.WithError(err).Error("Error encoding json")
|
||||
}
|
||||
SendStatus(http.StatusInternalServerError, w, description, l)
|
||||
}
|
||||
|
||||
// Tries to roll back a transaction, if it fails it logs the error
|
||||
func RollbackOrLogError(tx database.DBTransaction, l logrus.FieldLogger) {
|
||||
err := tx.Rollback()
|
||||
if err != nil {
|
||||
|
@ -85,6 +80,8 @@ func RollbackOrLogError(tx database.DBTransaction, l logrus.FieldLogger) {
|
|||
}
|
||||
}
|
||||
|
||||
// Checks if a user is banned by another user, then it returns Not Found to the client and the function returns false
|
||||
// Otherwise it returns true whithout sending anything to the client
|
||||
func SendNotFoundIfBanned(db database.AppDatabase, uid string, banner string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||
banned, err := db.IsBanned(uid, banner)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,10 +6,12 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
DEFAULT_LIMIT = 15
|
||||
DEFAULT_LIMIT = 30
|
||||
DEFAULT_OFFSET = 0
|
||||
)
|
||||
|
||||
// Get the start index and limit from the query.
|
||||
// If they are not present, use the default values.
|
||||
func GetLimits(query url.Values) (int, int, error) {
|
||||
|
||||
limit := DEFAULT_LIMIT
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Given a string, a regex and an error description, if the string doesn't match the regex, it sends a bad request error to the client and return false
|
||||
// Otherwise it returns true without sending anything to the client
|
||||
func MatchRegexOrBadRequest(str string, regex string, error_description string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||
|
||||
stat, err := regexp.Match(regex, []byte(str))
|
||||
|
@ -25,6 +27,7 @@ func MatchRegexOrBadRequest(str string, regex string, error_description string,
|
|||
return true
|
||||
}
|
||||
|
||||
// Validates a username (must be between 3 and 16 characters long and can only contain letters, numbers and underscores)
|
||||
func MatchUsernameOrBadRequest(username string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||
return MatchRegexOrBadRequest(username,
|
||||
`^[a-zA-Z0-9_]{3,16}$`, "Username must be between 3 and 16 characters long and can only contain letters, numbers and underscores",
|
||||
|
@ -32,6 +35,7 @@ func MatchUsernameOrBadRequest(username string, w http.ResponseWriter, l logrus.
|
|||
l)
|
||||
}
|
||||
|
||||
// Validates a comment (must be between 1 and 255 characters long)
|
||||
func MatchCommentOrBadRequest(comment string, w http.ResponseWriter, l logrus.FieldLogger) bool {
|
||||
return MatchRegexOrBadRequest(comment,
|
||||
`^(.){1,255}$`, "Comment must be between 1 and 255 characters long",
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||
)
|
||||
|
||||
// liveness is an HTTP handler that checks the API server status. If the server cannot serve requests (e.g., some
|
||||
// resources are not ready), this should reply with HTTP Status 500. Otherwise, with HTTP Status 200
|
||||
func (rt *_router) liveness(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
/* Example of liveness check:
|
||||
if err := rt.DB.Ping(); err != nil {
|
||||
if err := rt.db.Ping(); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}*/
|
||||
}
|
||||
helpers.SendStatus(200, w, "Server is live!", rt.baseLogger)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprout
|
|||
}
|
||||
|
||||
path := rt.dataPath + "/photos/" + uid + "/" + strconv.FormatInt(photo_id, 10) + ".jpg"
|
||||
// todo: we should check if the body is a valid jpg image
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { // perms = 511
|
||||
helpers.SendInternalError(err, "Error creating directory", w, rt.baseLogger)
|
||||
|
@ -78,7 +77,6 @@ func (rt *_router) PostPhoto(w http.ResponseWriter, r *http.Request, ps httprout
|
|||
|
||||
if err != nil {
|
||||
helpers.SendInternalError(err, "Error committing transaction", w, rt.baseLogger)
|
||||
//todo: should I roll back?
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -156,7 +154,7 @@ func (rt *_router) DeletePhoto(w http.ResponseWriter, r *http.Request, ps httpro
|
|||
if err != nil {
|
||||
helpers.SendInternalError(err, "Error deleting photo from database", w, rt.baseLogger)
|
||||
return
|
||||
} // todo: maybe let's use a transaction also here
|
||||
}
|
||||
|
||||
if !deleted {
|
||||
helpers.SendNotFound(w, "Photo not found", rt.baseLogger)
|
||||
|
|
|
@ -11,9 +11,17 @@ const (
|
|||
USER_NOT_FOUND = 3
|
||||
)
|
||||
|
||||
// Authorization is the interface for an authorization provider
|
||||
type Authorization interface {
|
||||
// Returns the type of the authorization provider
|
||||
GetType() string
|
||||
|
||||
// Returns the ID of the currently logged in user
|
||||
GetUserID() string
|
||||
|
||||
// Checks if the token is valid
|
||||
Authorized(db database.AppDatabase) (AuthStatus, error)
|
||||
|
||||
// Checks if the given user and the currently logged in user are the same user
|
||||
UserAuthorized(db database.AppDatabase, uid string) (AuthStatus, error)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue