Login technically works
This commit is contained in:
+84
-33
@@ -1,21 +1,29 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.phc.dm.unipi.it/aziis98/posti-dm/server/httputil"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoUserForSession = errors.New(`no user for session token`)
|
||||
)
|
||||
var SessionCookieName = "session" // TODO: Make configurable
|
||||
|
||||
type AuthMiddlewareConfig struct {
|
||||
IsLogged bool
|
||||
// RequireLogged rejects not logged users if true
|
||||
RequireLogged bool
|
||||
|
||||
// WithPermissions is the list of permissions the user should have to pass the middleware
|
||||
WithPermissions []string
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
// Login adds a session cookie to the user
|
||||
Login(w http.ResponseWriter, userID string)
|
||||
// Login checks user credentials and adds a session cookie to the user if successfull
|
||||
Login(w http.ResponseWriter, r *http.Request, userID, password string)
|
||||
// Logout clears the user session cookies (by setting the session cookie timeout to 0)
|
||||
Logout(w http.ResponseWriter)
|
||||
|
||||
@@ -25,71 +33,106 @@ type Authenticator interface {
|
||||
LoggedMiddleware() func(http.Handler) http.Handler
|
||||
|
||||
// UserFromSession returns the userID for this cookie session token
|
||||
UserFromSession(r http.Request) (string, error)
|
||||
UserFromSession(r *http.Request) (string, error)
|
||||
}
|
||||
|
||||
type AuthService struct {
|
||||
// CheckUserPassword
|
||||
CheckUserPassword func(userID string, password string) bool
|
||||
CheckUserPassword func(userID string, password string) error
|
||||
|
||||
// GetUserPermissions gets the list of permissions for this user
|
||||
UserPermissions func(userID string) []string
|
||||
UserPermissions func(userID string) ([]string, error)
|
||||
|
||||
// SessionTokenFromUser returns a session token that represents this user
|
||||
SessionTokenFromUser func(userID string) string
|
||||
// UserFromSessionToken returns the corresponing user for this session token
|
||||
UserFromSessionToken func(session string) (string, bool)
|
||||
SessionTokenFromUser func(userID string) (string, error)
|
||||
// UserFromSessionToken returns the corresponing user for this session token or "auth.ErrNoUserForSession"
|
||||
UserFromSessionToken func(session string) (string, error)
|
||||
|
||||
// AuthenticationFailed handles failed authentications
|
||||
AuthenticationFailed http.Handler
|
||||
|
||||
// OtherError handles other errors
|
||||
OtherError func(error) http.Handler
|
||||
}
|
||||
|
||||
func (auth *AuthService) Login(w http.ResponseWriter, userID string) {
|
||||
func (auth *AuthService) Login(w http.ResponseWriter, r *http.Request, userID, password string) {
|
||||
if err := auth.CheckUserPassword(userID, password); err != nil {
|
||||
auth.AuthenticationFailed.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := auth.SessionTokenFromUser(userID)
|
||||
if err != nil {
|
||||
auth.OtherError(err).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: SessionCookieName,
|
||||
Value: auth.SessionTokenFromUser(userID),
|
||||
Expires: time.Now().Add(10 * time.Minute), // TODO: Make configurable
|
||||
Path: "/", // TODO: Make configurable
|
||||
Value: token,
|
||||
Expires: time.Now().Add(7 * 24 * time.Hour), // TODO: Make configurable
|
||||
})
|
||||
|
||||
httputil.WriteJSON(w, "ok")
|
||||
}
|
||||
|
||||
func (auth *AuthService) Logout(w http.ResponseWriter) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: SessionCookieName,
|
||||
Path: "/",
|
||||
Value: "",
|
||||
Expires: time.Now(),
|
||||
})
|
||||
|
||||
httputil.WriteJSON(w, "ok")
|
||||
}
|
||||
|
||||
func (auth *AuthService) Middleware(config *AuthMiddlewareConfig) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie, err := r.Cookie(SessionCookieName)
|
||||
if err == http.ErrNoCookie && !config.IsLogged {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
if err == http.ErrNoCookie {
|
||||
if !config.RequireLogged { // Login not required
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
auth.AuthenticationFailed.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
userID, exists := auth.UserFromSessionToken(cookie.Value)
|
||||
if !exists {
|
||||
auth.Logout(w)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if err != nil {
|
||||
auth.OtherError(err).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if config.IsLogged {
|
||||
userPerms := map[string]bool{}
|
||||
for _, perm := range auth.UserPermissions(userID) {
|
||||
userPerms[perm] = true
|
||||
userID, err := auth.UserFromSessionToken(cookie.Value)
|
||||
if err == ErrNoUserForSession {
|
||||
auth.Logout(w)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
auth.OtherError(err).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if config.RequireLogged {
|
||||
userPerms, err := auth.UserPermissions(userID)
|
||||
if err != nil {
|
||||
auth.OtherError(err).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
userPermsMap := map[string]bool{}
|
||||
for _, perm := range userPerms {
|
||||
userPermsMap[perm] = true
|
||||
}
|
||||
|
||||
// check the user has all the permissions to access the route
|
||||
hasAll := true
|
||||
for _, perm := range config.WithPermissions {
|
||||
if _, present := userPerms[perm]; !present {
|
||||
if _, present := userPermsMap[perm]; !present {
|
||||
hasAll = false
|
||||
break
|
||||
}
|
||||
@@ -101,6 +144,14 @@ func (auth *AuthService) Middleware(config *AuthMiddlewareConfig) func(http.Hand
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh session cookie expiration
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: SessionCookieName,
|
||||
Path: "/",
|
||||
Value: cookie.Value,
|
||||
Expires: time.Now().Add(7 * 24 * time.Hour),
|
||||
})
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -108,20 +159,20 @@ func (auth *AuthService) Middleware(config *AuthMiddlewareConfig) func(http.Hand
|
||||
|
||||
func (auth *AuthService) LoggedMiddleware() func(http.Handler) http.Handler {
|
||||
return auth.Middleware(&AuthMiddlewareConfig{
|
||||
IsLogged: true,
|
||||
RequireLogged: true,
|
||||
WithPermissions: []string{},
|
||||
})
|
||||
}
|
||||
|
||||
func (auth *AuthService) UserFromSession(r http.Request) (string, error) {
|
||||
func (auth *AuthService) UserFromSession(r *http.Request) (string, error) {
|
||||
cookie, err := r.Cookie(SessionCookieName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
userID, exists := auth.UserFromSessionToken(cookie.Value)
|
||||
if !exists {
|
||||
return "", fmt.Errorf(`no user for this session token`)
|
||||
userID, err := auth.UserFromSessionToken(cookie.Value)
|
||||
if err == ErrNoUserForSession {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return userID, nil
|
||||
|
||||
+159
-29
@@ -1,50 +1,180 @@
|
||||
package db
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Entities
|
||||
|
||||
type Utente struct {
|
||||
type User struct {
|
||||
ID string
|
||||
Username string
|
||||
Permissions []string
|
||||
}
|
||||
|
||||
type Stanza struct {
|
||||
ID string
|
||||
Posti []string
|
||||
type Room struct {
|
||||
ID string
|
||||
SeatIDs []string
|
||||
|
||||
// gridRows, gridCols int
|
||||
}
|
||||
|
||||
// Actions
|
||||
type Seat struct {
|
||||
ID string
|
||||
|
||||
type AzioneBase struct {
|
||||
Tipo string
|
||||
}
|
||||
// OccupiedBy is an empty list or a singleton of a userID
|
||||
OccupiedBy []string
|
||||
|
||||
type AzioneStanza struct {
|
||||
AzioneBase
|
||||
UserID string
|
||||
StanzaID string
|
||||
}
|
||||
|
||||
type AzioneOccupaPosto struct {
|
||||
AzioneStanza
|
||||
PostoID string
|
||||
}
|
||||
|
||||
type AzioneLiberaPosto struct {
|
||||
AzioneStanza
|
||||
PostoID string
|
||||
// x, y, w, h int
|
||||
}
|
||||
|
||||
// Database Interfaces
|
||||
|
||||
type Store interface {
|
||||
// Entities
|
||||
GetUser(userID string) (*User, error)
|
||||
GetRoom(roomID string) (*Room, error)
|
||||
GetSeat(seatID string) (*Seat, error)
|
||||
|
||||
GetUtente(userID string) *Utente
|
||||
GetStanza(stanzaID string) *Stanza
|
||||
GetOccupiedSeats(roomID string) ([]string, error)
|
||||
GetFreeSeats(roomID string) ([]string, error)
|
||||
GetUserSeat(userID string) ([]string, error)
|
||||
|
||||
// Available Seats
|
||||
|
||||
GetPostiOccupati(stanzaID string) []string
|
||||
GetPostiLiberi(stanzaID string) []string
|
||||
OccupySeat(userID, roomID, seatID string) error
|
||||
FreeSeat(userID, roomID, seatID string) error
|
||||
}
|
||||
|
||||
// TODO: Create an SQLite implementation
|
||||
|
||||
type memDB struct {
|
||||
users map[string]*User
|
||||
rooms map[string]*Room
|
||||
seats map[string]*Seat
|
||||
}
|
||||
|
||||
func NewInMemoryStore() Store {
|
||||
db := &memDB{
|
||||
make(map[string]*User),
|
||||
make(map[string]*Room),
|
||||
make(map[string]*Seat),
|
||||
}
|
||||
|
||||
db.rooms["aula-stud"] = &Room{
|
||||
ID: "aula-stud",
|
||||
SeatIDs: []string{
|
||||
"aula-stud/posto-1",
|
||||
"aula-stud/posto-2",
|
||||
"aula-stud/posto-3",
|
||||
"aula-stud/posto-4",
|
||||
},
|
||||
}
|
||||
|
||||
db.seats["aula-stud/posto-1"] = &Seat{
|
||||
ID: "aula-stud/posto-1",
|
||||
OccupiedBy: []string{},
|
||||
}
|
||||
db.seats["aula-stud/posto-2"] = &Seat{
|
||||
ID: "aula-stud/posto-2",
|
||||
OccupiedBy: []string{},
|
||||
}
|
||||
db.seats["aula-stud/posto-3"] = &Seat{
|
||||
ID: "aula-stud/posto-3",
|
||||
OccupiedBy: []string{},
|
||||
}
|
||||
db.seats["aula-stud/posto-4"] = &Seat{
|
||||
ID: "aula-stud/posto-4",
|
||||
OccupiedBy: []string{},
|
||||
}
|
||||
|
||||
db.users["aziis98"] = &User{
|
||||
ID: "aziis98",
|
||||
Username: "aziis98",
|
||||
Permissions: []string{"admin"},
|
||||
}
|
||||
|
||||
db.users["bachoseven"] = &User{
|
||||
ID: "bachoseven",
|
||||
Username: "bachoseven",
|
||||
Permissions: []string{"admin"},
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *memDB) GetUser(userID string) (*User, error) {
|
||||
user, present := db.users[userID]
|
||||
if !present {
|
||||
return nil, fmt.Errorf(`no such user "%s"`, userID)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (db *memDB) GetRoom(roomID string) (*Room, error) {
|
||||
room, present := db.rooms[roomID]
|
||||
if !present {
|
||||
return nil, fmt.Errorf(`no such room "%s"`, roomID)
|
||||
}
|
||||
|
||||
return room, nil
|
||||
}
|
||||
|
||||
func (db *memDB) GetSeat(seatID string) (*Seat, error) {
|
||||
seat, present := db.seats[seatID]
|
||||
if !present {
|
||||
return nil, fmt.Errorf(`no such seat "%s"`, seatID)
|
||||
}
|
||||
|
||||
return seat, nil
|
||||
}
|
||||
|
||||
func (db *memDB) GetOccupiedSeats(roomID string) ([]string, error) {
|
||||
room, err := db.GetRoom(roomID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seats := []string{}
|
||||
for _, seatID := range room.SeatIDs {
|
||||
seat := db.seats[seatID]
|
||||
if len(seat.OccupiedBy) == 1 {
|
||||
seats = append(seats, seatID)
|
||||
}
|
||||
}
|
||||
|
||||
return seats, nil
|
||||
}
|
||||
|
||||
func (db *memDB) GetFreeSeats(roomID string) ([]string, error) {
|
||||
room, err := db.GetRoom(roomID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seats := []string{}
|
||||
for _, seatID := range room.SeatIDs {
|
||||
seat := db.seats[seatID]
|
||||
if len(seat.OccupiedBy) == 0 {
|
||||
seats = append(seats, seatID)
|
||||
}
|
||||
}
|
||||
|
||||
return seats, nil
|
||||
}
|
||||
|
||||
func (db *memDB) GetUserSeat(userID string) ([]string, error) {
|
||||
for _, seat := range db.seats {
|
||||
if len(seat.OccupiedBy) > 0 && seat.OccupiedBy[0] == userID {
|
||||
return []string{userID}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (db *memDB) OccupySeat(userID string, roomID string, seatID string) error {
|
||||
db.seats[seatID].OccupiedBy = []string{userID}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *memDB) FreeSeat(userID string, roomID string, seatID string) error {
|
||||
db.seats[seatID].OccupiedBy = []string{}
|
||||
return nil
|
||||
}
|
||||
|
||||
+4
-1
@@ -4,4 +4,7 @@ go 1.17
|
||||
|
||||
require github.com/go-chi/chi/v5 v5.0.7
|
||||
|
||||
require github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
||||
require (
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.12 // indirect
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
|
||||
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type H map[string]interface{}
|
||||
|
||||
func WriteJSON(w http.ResponseWriter, data interface{}) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
return json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
func ReadJSON(r *http.Request, data interface{}) error {
|
||||
return json.NewDecoder(r.Body).Decode(data)
|
||||
}
|
||||
@@ -2,37 +2,101 @@ package httputil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func HandleSSE(handler func(broadcast, single chan<- string)) http.Handler {
|
||||
broadcast := make(chan string)
|
||||
type SSEHandler struct {
|
||||
clients map[chan string]bool
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
Connected func(chan string)
|
||||
Disconnected func(chan string)
|
||||
}
|
||||
|
||||
client := make(chan string)
|
||||
defer func() {
|
||||
close(client)
|
||||
}()
|
||||
func (sse *SSEHandler) init() {
|
||||
if sse.clients == nil {
|
||||
sse.clients = make(map[chan string]bool)
|
||||
}
|
||||
}
|
||||
|
||||
go handler(broadcast, client)
|
||||
func (sse *SSEHandler) Broadcast(message string) {
|
||||
sse.init()
|
||||
|
||||
flusher, _ := w.(http.Flusher)
|
||||
for {
|
||||
select {
|
||||
case message := <-broadcast:
|
||||
fmt.Fprintf(w, "%s\n", message)
|
||||
flusher.Flush()
|
||||
case message := <-client:
|
||||
fmt.Fprintf(w, "%s\n", message)
|
||||
flusher.Flush()
|
||||
case <-r.Context().Done():
|
||||
for client := range sse.clients {
|
||||
client <- message
|
||||
}
|
||||
}
|
||||
|
||||
func (sse *SSEHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
sse.init()
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
client := make(chan string)
|
||||
sse.clients[client] = true
|
||||
|
||||
log.Printf(`New connection`)
|
||||
|
||||
if sse.Connected != nil {
|
||||
go sse.Connected(client)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
log.Printf(`Connection closed`)
|
||||
close(client)
|
||||
delete(sse.clients, client)
|
||||
if sse.Disconnected != nil {
|
||||
go sse.Disconnected(client)
|
||||
}
|
||||
}()
|
||||
|
||||
flusher, _ := w.(http.Flusher)
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-client:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "data: %s\n\n", message)
|
||||
flusher.Flush()
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// func HandleSSE(handler func(broadcast, single chan<- string)) http.Handler {
|
||||
// broadcast := make(chan string)
|
||||
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Set("Content-Type", "text/event-stream")
|
||||
// w.Header().Set("Cache-Control", "no-cache")
|
||||
// w.Header().Set("Connection", "keep-alive")
|
||||
// w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
// client := make(chan string)
|
||||
// defer func() {
|
||||
// close(client)
|
||||
// }()
|
||||
|
||||
// go handler(broadcast, client)
|
||||
|
||||
// flusher, _ := w.(http.Flusher)
|
||||
// for {
|
||||
// select {
|
||||
// case message := <-broadcast:
|
||||
// fmt.Fprintf(w, "data: %s\n\n", message)
|
||||
// flusher.Flush()
|
||||
// case message := <-client:
|
||||
// fmt.Fprintf(w, "data: %s\n\n", message)
|
||||
// flusher.Flush()
|
||||
// case <-r.Context().Done():
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
+12
-11
@@ -1,6 +1,7 @@
|
||||
package httputil_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -9,23 +10,23 @@ import (
|
||||
)
|
||||
|
||||
func TestSSE(t *testing.T) {
|
||||
var broadcastChannel chan<- string
|
||||
sse := &httputil.SSEHandler{
|
||||
Connected: func(client chan string) {
|
||||
log.Printf(`New client connected callback`)
|
||||
client <- "Messaggio 1 per questo client"
|
||||
time.Sleep(1 * time.Second)
|
||||
client <- "Messaggio 2 per questo client"
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
if broadcastChannel != nil {
|
||||
broadcastChannel <- "Messaggio per tutti"
|
||||
}
|
||||
log.Printf(`Broadcasting message`)
|
||||
sse.Broadcast("Messaggio per tutti")
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
http.Handle("/sse", httputil.HandleSSE(func(broadcast, single chan<- string) {
|
||||
broadcastChannel = broadcast
|
||||
time.Sleep(1 * time.Second)
|
||||
single <- "Messaggio 1 per questo client"
|
||||
single <- "Messaggio 2 per questo client"
|
||||
}))
|
||||
|
||||
http.Handle("/sse", sse)
|
||||
http.ListenAndServe(":8000", nil)
|
||||
}
|
||||
|
||||
+18
-1
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
)
|
||||
|
||||
var HOST = ":3000"
|
||||
var HOST = ":4000"
|
||||
|
||||
func main() {
|
||||
r := chi.NewRouter()
|
||||
@@ -18,6 +18,23 @@ func main() {
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
server := NewServer()
|
||||
|
||||
// api := chi.NewRouter()
|
||||
// api.Get("/example", func(w http.ResponseWriter, r *http.Request) {
|
||||
// w.Header().Add("Content-Type", "application/json")
|
||||
// http.SetCookie(w, &http.Cookie{
|
||||
// Name: "test",
|
||||
// Value: "Prova",
|
||||
// Expires: time.Now().Add(120 * time.Second),
|
||||
// })
|
||||
// json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
// "foo": "bar",
|
||||
// })
|
||||
// })
|
||||
|
||||
r.Mount("/api", server.ApiRoute)
|
||||
|
||||
log.Printf(`Starting server on %s...`, HOST)
|
||||
err := http.ListenAndServe(HOST, r)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.phc.dm.unipi.it/aziis98/posti-dm/server/auth"
|
||||
"git.phc.dm.unipi.it/aziis98/posti-dm/server/db"
|
||||
"git.phc.dm.unipi.it/aziis98/posti-dm/server/httputil"
|
||||
"git.phc.dm.unipi.it/aziis98/posti-dm/server/util"
|
||||
"github.com/alecthomas/repr"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
sessions map[string]*db.User
|
||||
authService *auth.AuthService
|
||||
|
||||
Database db.Store
|
||||
ApiRoute chi.Router
|
||||
}
|
||||
|
||||
func NewServer() *Server {
|
||||
server := &Server{
|
||||
sessions: make(map[string]*db.User),
|
||||
Database: db.NewInMemoryStore(),
|
||||
ApiRoute: chi.NewRouter(),
|
||||
}
|
||||
|
||||
server.authService = &auth.AuthService{
|
||||
CheckUserPassword: func(userID, password string) error {
|
||||
repr.Println("Sessions: ", server.sessions)
|
||||
|
||||
if password != "phc" {
|
||||
return fmt.Errorf(`invalid password`)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
UserPermissions: func(userID string) ([]string, error) {
|
||||
user, err := server.Database.GetUser(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user.Permissions, nil
|
||||
},
|
||||
SessionTokenFromUser: func(userID string) (string, error) {
|
||||
user, err := server.Database.GetUser(userID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := util.RandomHash(10)
|
||||
server.sessions[token] = user
|
||||
|
||||
return token, nil
|
||||
},
|
||||
UserFromSessionToken: func(session string) (string, error) {
|
||||
user, present := server.sessions[session]
|
||||
if !present {
|
||||
return "", auth.ErrNoUserForSession
|
||||
}
|
||||
|
||||
return user.ID, nil
|
||||
},
|
||||
AuthenticationFailed: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, `not authenticated`, http.StatusUnauthorized)
|
||||
}),
|
||||
OtherError: func(err error) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
server.setupRoutes()
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func (server *Server) setupRoutes() {
|
||||
api := server.ApiRoute
|
||||
db := server.Database
|
||||
|
||||
// Authenticated Routes
|
||||
|
||||
api.Post("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
var credentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if err := httputil.ReadJSON(r, &credentials); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
server.authService.Login(w, r, credentials.Username, credentials.Password)
|
||||
})
|
||||
|
||||
api.With(server.authService.LoggedMiddleware()).
|
||||
Post("/logout", func(w http.ResponseWriter, r *http.Request) {
|
||||
server.authService.Logout(w)
|
||||
})
|
||||
|
||||
api.With(server.authService.LoggedMiddleware()).
|
||||
Get("/current-seat", func(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := server.authService.UserFromSession(r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
occupiedSeatID, err := db.GetUserSeat(userID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if len(occupiedSeatID) == 0 {
|
||||
httputil.WriteJSON(w, nil)
|
||||
return
|
||||
}
|
||||
|
||||
seat, err := db.GetSeat(occupiedSeatID[0])
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
httputil.WriteJSON(w, seat)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func RandomHash(len int) string {
|
||||
buff := make([]byte, len)
|
||||
rand.Read(buff)
|
||||
str := base64.StdEncoding.EncodeToString(buff)
|
||||
return str[:len]
|
||||
}
|
||||
Reference in New Issue
Block a user