Small auth module refactor

main-old
Antonio De Lucreziis 2 years ago
parent 45d41d714b
commit 0520e7641f

@ -1,54 +1,32 @@
package auth package auth
import "fmt" import (
"fmt"
// ErrInvalidSession is thrown when an AuthenticatorService is given a missing or invalid session token "git.phc.dm.unipi.it/phc/website/model"
var ErrInvalidSession = fmt.Errorf(`invalid session token`) )
// User represents a user returned from AuthenticatorService
type User struct {
Username string `json:"username"`
Name string `json:"name"`
Surname string `json:"surname"`
FullName string `json:"fullName"`
Email string `json:"email"`
}
// WithDefaultFullName is a utility that returns a copy of the given user with the full name set to the concatenation of the name and surname of the user.
func (u User) WithDefaultFullName() User {
return User{
Username: u.Username,
Name: u.Name,
Surname: u.Surname,
Email: u.Email,
FullName: u.Username + " " + u.Surname,
}
}
// Session represents a session returned from AuthenticatorService // ErrInvalidSession is thrown when an AuthenticatorService is given a missing
type Session struct { // or invalid session token
Username string `json:"username"` var ErrInvalidSession = fmt.Errorf(`invalid session token`)
Token string `json:"token"`
}
// AuthenticatorService can login users using a separate http service or a temporary in memory store. When a user logs in the auth service returns a session token that can be used to read and modify user properties without having to re-send the user password. (TODO: Not yet implemented, this token has to be renewed every so often otherwise it lasts just a couple of days) // Service is an authentication service abstraction. When a user is logged in a
type AuthenticatorService interface { // new session token is returned, this can be used to read and modify user
// properties without having to re-send the user password. (TODO: implement
// token renewal)
type Service interface {
// GetUser retrieves the user info given the username // GetUser retrieves the user info given the username
GetUser(username string) (*User, error) GetUser(username string) (*model.User, error)
// GetUsers retrieves the full user list from the authentication service // GetUsers retrieves the full user list from the authentication service
GetUsers() ([]*User, error) GetUsers() ([]*model.User, error)
// GetSession retrieves the user session associated to a session token // GetSession retrieves the user session associated to a session token
GetSession(token string) (*Session, error) GetSession(token string) (*model.Session, error)
// Login tries to log in a user given username and password and if successful returns a new user session // Login tries to log in a user given username and password and if successful returns a new user session
Login(username, password string) (*Session, error) Login(username, password string) (*model.Session, error)
} }
// UserForSession returns the user (object) linked to a session token, this is just a shortcut for calling [AuthenticatorService.GetSession] and then [AuthenticatorService.GetUser] // UserForSession returns the user linked to the given session token, this is just a shortcut for calling [AuthenticatorService.GetSession] and then [AuthenticatorService.GetUser]
func UserForSession(as AuthenticatorService, token string) (*User, error) { func UserForSession(as Service, token string) (*model.User, error) {
session, err := as.GetSession(token) session, err := as.GetSession(token)
if err != nil { if err != nil {
return nil, err return nil, err
@ -62,8 +40,10 @@ func UserForSession(as AuthenticatorService, token string) (*User, error) {
return user, nil return user, nil
} }
// New create an AuthenticatorService given an "host" string, if ":memory:" then this just returns an example AuthenticatorService using the [auth.Memory] implementation, otherwise for now this defaults to [auth.LDAPAuthService] // NewDefaultService create an AuthenticatorService given an "host" string,
func New(host string) AuthenticatorService { // If host is ":memory:" then this uses the [auth.Memory] implementation,
// otherwise for now this defaults to [auth.LDAPAuthService]
func NewDefaultService(host string) Service {
if host == ":memory:" { if host == ":memory:" {
return exampleMemoryUsers return exampleMemoryUsers
} }

@ -7,6 +7,8 @@ import (
"log" "log"
"net/http" "net/http"
"path" "path"
"git.phc.dm.unipi.it/phc/website/model"
) )
// ldapUser represents an LDAP User, most fields are inherited from [auth.User] // ldapUser represents an LDAP User, most fields are inherited from [auth.User]
@ -21,8 +23,8 @@ type ldapUser struct {
} }
// AsUser converts an [ldapUser] to an instance of [auth.User] // AsUser converts an [ldapUser] to an instance of [auth.User]
func (u ldapUser) AsUser() *User { func (u ldapUser) AsUser() *model.User {
return &User{ return &model.User{
Username: u.Uid, Username: u.Uid,
Name: u.Name, Name: u.Name,
Surname: u.Surname, Surname: u.Surname,
@ -36,6 +38,10 @@ type LDAPAuthService struct {
Host string Host string
} }
func NewLDAPAuthService(host string) Service {
return &LDAPAuthService{host}
}
// doGetRequest is a utility to make HTTP GET requests // doGetRequest is a utility to make HTTP GET requests
func (a *LDAPAuthService) doGetRequest(url string, response interface{}) error { func (a *LDAPAuthService) doGetRequest(url string, response interface{}) error {
req, err := http.NewRequest( req, err := http.NewRequest(
@ -83,7 +89,7 @@ func (a *LDAPAuthService) doPostRequest(url string, request interface{}, respons
return json.NewDecoder(res.Body).Decode(response) return json.NewDecoder(res.Body).Decode(response)
} }
func (a *LDAPAuthService) GetUser(username string) (*User, error) { func (a *LDAPAuthService) GetUser(username string) (*model.User, error) {
var user ldapUser var user ldapUser
if err := a.doGetRequest(fmt.Sprintf("/user/%s", username), &user); err != nil { if err := a.doGetRequest(fmt.Sprintf("/user/%s", username), &user); err != nil {
return nil, err return nil, err
@ -92,13 +98,13 @@ func (a *LDAPAuthService) GetUser(username string) (*User, error) {
return user.AsUser(), nil return user.AsUser(), nil
} }
func (a *LDAPAuthService) GetUsers() ([]*User, error) { func (a *LDAPAuthService) GetUsers() ([]*model.User, error) {
ldapUsers := []*ldapUser{} ldapUsers := []*ldapUser{}
if err := a.doGetRequest("/users", &ldapUsers); err != nil { if err := a.doGetRequest("/users", &ldapUsers); err != nil {
return nil, err return nil, err
} }
users := make([]*User, len(ldapUsers)) users := make([]*model.User, len(ldapUsers))
for i, u := range ldapUsers { for i, u := range ldapUsers {
users[i] = u.AsUser() users[i] = u.AsUser()
} }
@ -106,8 +112,8 @@ func (a *LDAPAuthService) GetUsers() ([]*User, error) {
return users, nil return users, nil
} }
func (a *LDAPAuthService) GetSession(token string) (*Session, error) { func (a *LDAPAuthService) GetSession(token string) (*model.Session, error) {
var response Session var response model.Session
if err := a.doGetRequest(fmt.Sprintf("/session/%s", token), &response); err != nil { if err := a.doGetRequest(fmt.Sprintf("/session/%s", token), &response); err != nil {
return nil, err return nil, err
} }
@ -115,13 +121,13 @@ func (a *LDAPAuthService) GetSession(token string) (*Session, error) {
return &response, nil return &response, nil
} }
func (a *LDAPAuthService) Login(username, password string) (*Session, error) { func (a *LDAPAuthService) Login(username, password string) (*model.Session, error) {
reqBody := map[string]interface{}{ reqBody := map[string]interface{}{
"username": username, "username": username,
"password": password, "password": password,
} }
var response Session var response model.Session
if err := a.doPostRequest("/login", reqBody, &response); err != nil { if err := a.doPostRequest("/login", reqBody, &response); err != nil {
return nil, err return nil, err
} }

@ -3,13 +3,14 @@ package auth
import ( import (
"fmt" "fmt"
"git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/util" "git.phc.dm.unipi.it/phc/website/util"
) )
var exampleMemoryUsers = &Memory{ var exampleMemoryUsers = &Memory{
Users: map[string]*memoryUser{ Users: map[string]*memoryUser{
"aziis98": { "aziis98": {
User: User{ User: model.User{
Username: "aziis98", Username: "aziis98",
Name: "Antonio", Name: "Antonio",
Surname: "De Lucreziis", Surname: "De Lucreziis",
@ -18,7 +19,7 @@ var exampleMemoryUsers = &Memory{
Password: "123", Password: "123",
}, },
"bachoseven": { "bachoseven": {
User: User{ User: model.User{
Username: "bachoseven", Username: "bachoseven",
Name: "Francesco", Name: "Francesco",
Surname: "Minnocci", Surname: "Minnocci",
@ -31,12 +32,12 @@ var exampleMemoryUsers = &Memory{
} }
type memoryUser struct { type memoryUser struct {
User model.User
Password string `json:"-"` Password string `json:"-"`
} }
func (u memoryUser) AsUser() *User { func (u memoryUser) AsUser() *model.User {
return &User{ return &model.User{
Username: u.Username, Username: u.Username,
Name: u.Name, Name: u.Name,
Surname: u.Surname, Surname: u.Surname,
@ -50,8 +51,8 @@ type memorySession struct {
Token string Token string
} }
func (s memorySession) AsSession() *Session { func (s memorySession) AsSession() *model.Session {
return &Session{ return &model.Session{
Username: s.Username, Username: s.Username,
Token: s.Token, Token: s.Token,
} }
@ -62,7 +63,7 @@ type Memory struct {
Sessions map[string]*memorySession Sessions map[string]*memorySession
} }
func (m *Memory) GetUser(username string) (*User, error) { func (m *Memory) GetUser(username string) (*model.User, error) {
user, ok := m.Users[username] user, ok := m.Users[username]
if !ok { if !ok {
return nil, fmt.Errorf(`no user with that username`) return nil, fmt.Errorf(`no user with that username`)
@ -71,8 +72,8 @@ func (m *Memory) GetUser(username string) (*User, error) {
return user.AsUser(), nil return user.AsUser(), nil
} }
func (m *Memory) GetUsers() ([]*User, error) { func (m *Memory) GetUsers() ([]*model.User, error) {
users := make([]*User, len(m.Users)) users := make([]*model.User, len(m.Users))
i := 0 i := 0
for _, u := range m.Users { for _, u := range m.Users {
users[i] = u.AsUser() users[i] = u.AsUser()
@ -82,7 +83,7 @@ func (m *Memory) GetUsers() ([]*User, error) {
return users, nil return users, nil
} }
func (m *Memory) GetSession(token string) (*Session, error) { func (m *Memory) GetSession(token string) (*model.Session, error) {
session, ok := m.Sessions[token] session, ok := m.Sessions[token]
if !ok { if !ok {
return nil, ErrInvalidSession return nil, ErrInvalidSession
@ -91,7 +92,7 @@ func (m *Memory) GetSession(token string) (*Session, error) {
return session.AsSession(), nil return session.AsSession(), nil
} }
func (m *Memory) Login(username string, password string) (*Session, error) { func (m *Memory) Login(username string, password string) (*model.Session, error) {
user, ok := m.Users[username] user, ok := m.Users[username]
if !ok { if !ok {
return nil, fmt.Errorf(`no user with that username`) return nil, fmt.Errorf(`no user with that username`)

@ -9,6 +9,7 @@ import (
"git.phc.dm.unipi.it/phc/website/articles" "git.phc.dm.unipi.it/phc/website/articles"
"git.phc.dm.unipi.it/phc/website/auth" "git.phc.dm.unipi.it/phc/website/auth"
"git.phc.dm.unipi.it/phc/website/config" "git.phc.dm.unipi.it/phc/website/config"
"git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/templates" "git.phc.dm.unipi.it/phc/website/templates"
"git.phc.dm.unipi.it/phc/website/util" "git.phc.dm.unipi.it/phc/website/util"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
@ -17,7 +18,7 @@ import (
"github.com/gofiber/redirect/v2" "github.com/gofiber/redirect/v2"
) )
func UserMiddleware(as auth.AuthenticatorService) fiber.Handler { func UserMiddleware(as auth.Service) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
token := c.Cookies("session-token") token := c.Cookies("session-token")
user, _ := auth.UserForSession(as, token) user, _ := auth.UserForSession(as, token)
@ -44,7 +45,7 @@ func main() {
// Serve content statically from "./public", mounted on the "/public/" route // Serve content statically from "./public", mounted on the "/public/" route
app.Static("/public/", "./public") app.Static("/public/", "./public")
authService := auth.New(config.AuthServiceHost) authService := auth.NewDefaultService(config.AuthServiceHost)
app.Use(UserMiddleware(authService)) app.Use(UserMiddleware(authService))
// Templates & Renderer // Templates & Renderer
@ -157,7 +158,7 @@ func main() {
}) })
app.Get("/profilo", func(c *fiber.Ctx) error { app.Get("/profilo", func(c *fiber.Ctx) error {
user, ok := c.Locals("user").(*auth.User) user, ok := c.Locals("user").(*model.User)
if !ok || user == nil { if !ok || user == nil {
return fmt.Errorf(`user not logged in`) return fmt.Errorf(`user not logged in`)
} }

@ -0,0 +1,30 @@
package model
// User represents a user returned from AuthenticatorService
type User struct {
Username string `json:"username"`
Name string `json:"name"`
Surname string `json:"surname"`
// FullName is a separate field from Name and Surname because for example
// ldap stores them all as separate fields.
FullName string `json:"fullName"`
Email string `json:"email"`
}
// WithDefaultFullName is a utility that returns a copy of the given user with the full name set to the concatenation of the name and surname of the user.
func (u User) WithDefaultFullName() User {
return User{
Username: u.Username,
Name: u.Name,
Surname: u.Surname,
Email: u.Email,
FullName: u.Username + " " + u.Surname,
}
}
// Session represents a session returned from AuthenticatorService
type Session struct {
Username string `json:"username"`
Token string `json:"token"`
}
Loading…
Cancel
Save