identity providers and bearerauth

This commit is contained in:
Marco Realacci 2022-11-18 13:05:40 +01:00
parent 5f3d4df33a
commit 626b7fa3e9
32 changed files with 1317 additions and 12 deletions

View file

@ -1,11 +1,13 @@
package api
import (
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
"net/http"
"github.com/gofrs/uuid"
"github.com/julienschmidt/httprouter"
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
"github.com/sirupsen/logrus"
"net/http"
)
// httpRouterHandler is the signature for functions that accepts a reqcontext.RequestContext in addition to those
@ -21,8 +23,18 @@ func (rt *_router) wrap(fn httpRouterHandler) func(http.ResponseWriter, *http.Re
w.WriteHeader(http.StatusInternalServerError)
return
}
auth, err := authorization.BuildAuth(r.Header.Get("Authorization"))
if err != nil {
rt.baseLogger.WithError(err).Info("User not authorized")
w.WriteHeader(http.StatusUnauthorized)
return
}
var ctx = reqcontext.RequestContext{
ReqUUID: reqUUID,
Auth: auth,
}
// Create a request-specific logger

View file

@ -7,6 +7,7 @@ import (
// Handler returns an instance of httprouter.Router that handle APIs registered here
func (rt *_router) Handler() http.Handler {
// Register routes
rt.router.POST("/session", rt.wrap(rt.PostSession))
rt.router.GET("/", rt.getHelloWorld)
rt.router.GET("/context", rt.wrap(rt.getContextReply))

View file

@ -0,0 +1,50 @@
package authorization
import (
"errors"
"strings"
"github.com/notherealmarco/WASAPhoto/service/database"
)
type BearerAuth struct {
token string
}
func (b *BearerAuth) GetType() string {
return "Bearer"
}
func BuildBearer(header string) (*BearerAuth, error) {
if header == "" {
return nil, errors.New("missing authorization header")
}
if header == "Bearer" {
return nil, errors.New("missing token")
}
if !strings.HasPrefix(header, "Bearer ") {
return nil, errors.New("invalid authorization header")
}
return &BearerAuth{token: header[7:]}, nil
}
func (b *BearerAuth) GetToken() string {
return b.token
}
func (b *BearerAuth) Authorized(db database.AppDatabase) (bool, error) {
// this is the way we manage authorization, the bearer token is the user id
state, err := db.UserExists(b.token)
if err != nil {
return false, err
}
return state, nil
}
func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (bool, error) {
if b.token == uid {
return b.Authorized(db)
}
return false, nil
}

View file

@ -0,0 +1,18 @@
package authorization
import (
"errors"
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
)
func BuildAuth(header string) (reqcontext.Authorization, error) {
auth, err := BuildBearer(header)
if err != nil {
if err.Error() == "invalid authorization header" {
return nil, errors.New("method not supported") // todo: better error description
}
return nil, err
}
return auth, nil
}

View file

@ -6,6 +6,7 @@ import (
"github.com/julienschmidt/httprouter"
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
"github.com/notherealmarco/WASAPhoto/service/database/db_errors"
)
type _reqbody struct {
@ -21,15 +22,26 @@ type _respbody struct {
func (rt *_router) PostSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
var request _reqbody
json.NewDecoder(r.Body).Decode(&request) //todo: capire se serve close
err := json.NewDecoder(r.Body).Decode(&request) //todo: capire se serve close
uid, err := rt.db.GetUserID(request.Name)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
var uid string
if err == nil { // test if user exists
uid, err = rt.db.GetUserID(request.Name)
}
if db_errors.EmptySet(err) { // user does not exist
err = nil
uid, err = rt.db.CreateUser(request.Name)
}
if err != nil { // handle any other error
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok
return
}
w.Header().Set("content-type", "application/json")
json.NewEncoder(w).Encode(_respbody{UID: uid})
err = json.NewEncoder(w).Encode(_respbody{UID: uid})
if err != nil {
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok
return
}
}

View file

@ -0,0 +1,14 @@
package api
import (
"net/http"
"github.com/julienschmidt/httprouter"
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
)
func (rt *_router) UpdateUsername(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
auth, err := ctx.Auth.UserAuthorized(rt.db, r.URL.Path // todo: prendere il coso giusto dal path)
}

View file

@ -8,6 +8,7 @@ package reqcontext
import (
"github.com/gofrs/uuid"
"github.com/notherealmarco/WASAPhoto/service/database"
"github.com/sirupsen/logrus"
)
@ -18,4 +19,12 @@ type RequestContext struct {
// Logger is a custom field logger for the request
Logger logrus.FieldLogger
Auth Authorization
}
type Authorization interface {
GetType() string
Authorized(db database.AppDatabase) (bool, error)
UserAuthorized(db database.AppDatabase, uid string) (bool, error)
}

View file

@ -38,9 +38,10 @@ import (
// AppDatabase is the high level interface for the DB
type AppDatabase interface {
UserExists(uid string) (bool, error)
GetUserID(name string) (string, error)
SetName(name string) error
CreateUser(uid string, name string) error
CreateUser(name string) (string, error)
FollowUser(uid string, follow string) error
UnfollowUser(uid string, unfollow string) error
BanUser(uid string, ban string) error

View file

@ -1,8 +1,26 @@
package database
import (
"github.com/gofrs/uuid"
"github.com/notherealmarco/WASAPhoto/service/database/db_errors"
)
//Check if user exists and if exists return the user id by username
//todo
// Check if user exists
func (db *appdbimpl) UserExists(uid string) (bool, error) {
var name string
err := db.c.QueryRow(`SELECT "name" FROM "users" WHERE "uid" = ?`, name).Scan(&name)
if db_errors.EmptySet(err) {
return false, nil
} else if err != nil {
return false, err
}
return true, nil
}
// Get user id by username
func (db *appdbimpl) GetUserID(name string) (string, error) {
var uid string
@ -11,9 +29,13 @@ func (db *appdbimpl) GetUserID(name string) (string, error) {
}
// Create a new user
func (db *appdbimpl) CreateUser(uid string, name string) error {
_, err := db.c.Exec(`INSERT INTO "users" ("uid", "name") VALUES (?, ?)`, uid, name)
return err
func (db *appdbimpl) CreateUser(name string) (string, error) {
uid, err := uuid.NewV4()
if err != nil {
return "", err
}
_, err = db.c.Exec(`INSERT INTO "users" ("uid", "name") VALUES (?, ?)`, uid.String(), name)
return uid.String(), err
}
// Follow a user

View file

@ -0,0 +1,11 @@
package db_errors
import "strings"
// Returns true if the query result has no rows
func EmptySet(err error) bool {
if err == nil {
return false
}
return strings.Contains(err.Error(), "no rows in result set")
}