Small auth module refactor

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

@ -1,54 +1,32 @@
package auth
import "fmt"
import (
"fmt"
// ErrInvalidSession is thrown when an AuthenticatorService is given a missing or invalid session token
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,
}
}
"git.phc.dm.unipi.it/phc/website/model"
)
// Session represents a session returned from AuthenticatorService
type Session struct {
Username string `json:"username"`
Token string `json:"token"`
}
// ErrInvalidSession is thrown when an AuthenticatorService is given a missing
// or invalid session token
var ErrInvalidSession = fmt.Errorf(`invalid session 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)
type AuthenticatorService interface {
// Service is an authentication service abstraction. When a user is logged in a
// 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(username string) (*User, error)
GetUser(username string) (*model.User, error)
// 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(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(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]
func UserForSession(as AuthenticatorService, token string) (*User, error) {
// 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 Service, token string) (*model.User, error) {
session, err := as.GetSession(token)
if err != nil {
return nil, err
@ -62,8 +40,10 @@ func UserForSession(as AuthenticatorService, token string) (*User, error) {
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]
func New(host string) AuthenticatorService {
// NewDefaultService create an AuthenticatorService given an "host" string,
// 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:" {
return exampleMemoryUsers
}

@ -7,6 +7,8 @@ import (
"log"
"net/http"
"path"
"git.phc.dm.unipi.it/phc/website/model"
)
// 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]
func (u ldapUser) AsUser() *User {
return &User{
func (u ldapUser) AsUser() *model.User {
return &model.User{
Username: u.Uid,
Name: u.Name,
Surname: u.Surname,
@ -36,6 +38,10 @@ type LDAPAuthService struct {
Host string
}
func NewLDAPAuthService(host string) Service {
return &LDAPAuthService{host}
}
// doGetRequest is a utility to make HTTP GET requests
func (a *LDAPAuthService) doGetRequest(url string, response interface{}) error {
req, err := http.NewRequest(
@ -83,7 +89,7 @@ func (a *LDAPAuthService) doPostRequest(url string, request interface{}, respons
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
if err := a.doGetRequest(fmt.Sprintf("/user/%s", username), &user); err != nil {
return nil, err
@ -92,13 +98,13 @@ func (a *LDAPAuthService) GetUser(username string) (*User, error) {
return user.AsUser(), nil
}
func (a *LDAPAuthService) GetUsers() ([]*User, error) {
func (a *LDAPAuthService) GetUsers() ([]*model.User, error) {
ldapUsers := []*ldapUser{}
if err := a.doGetRequest("/users", &ldapUsers); err != nil {
return nil, err
}
users := make([]*User, len(ldapUsers))
users := make([]*model.User, len(ldapUsers))
for i, u := range ldapUsers {
users[i] = u.AsUser()
}
@ -106,8 +112,8 @@ func (a *LDAPAuthService) GetUsers() ([]*User, error) {
return users, nil
}
func (a *LDAPAuthService) GetSession(token string) (*Session, error) {
var response Session
func (a *LDAPAuthService) GetSession(token string) (*model.Session, error) {
var response model.Session
if err := a.doGetRequest(fmt.Sprintf("/session/%s", token), &response); err != nil {
return nil, err
}
@ -115,13 +121,13 @@ func (a *LDAPAuthService) GetSession(token string) (*Session, error) {
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{}{
"username": username,
"password": password,
}
var response Session
var response model.Session
if err := a.doPostRequest("/login", reqBody, &response); err != nil {
return nil, err
}

@ -3,13 +3,14 @@ package auth
import (
"fmt"
"git.phc.dm.unipi.it/phc/website/model"
"git.phc.dm.unipi.it/phc/website/util"
)
var exampleMemoryUsers = &Memory{
Users: map[string]*memoryUser{
"aziis98": {
User: User{
User: model.User{
Username: "aziis98",
Name: "Antonio",
Surname: "De Lucreziis",
@ -18,7 +19,7 @@ var exampleMemoryUsers = &Memory{
Password: "123",
},
"bachoseven": {
User: User{
User: model.User{
Username: "bachoseven",
Name: "Francesco",
Surname: "Minnocci",
@ -31,12 +32,12 @@ var exampleMemoryUsers = &Memory{
}
type memoryUser struct {
User
model.User
Password string `json:"-"`
}
func (u memoryUser) AsUser() *User {
return &User{
func (u memoryUser) AsUser() *model.User {
return &model.User{
Username: u.Username,
Name: u.Name,
Surname: u.Surname,
@ -50,8 +51,8 @@ type memorySession struct {
Token string
}
func (s memorySession) AsSession() *Session {
return &Session{
func (s memorySession) AsSession() *model.Session {
return &model.Session{
Username: s.Username,
Token: s.Token,
}
@ -62,7 +63,7 @@ type Memory struct {
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]
if !ok {
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
}
func (m *Memory) GetUsers() ([]*User, error) {
users := make([]*User, len(m.Users))
func (m *Memory) GetUsers() ([]*model.User, error) {
users := make([]*model.User, len(m.Users))
i := 0
for _, u := range m.Users {
users[i] = u.AsUser()
@ -82,7 +83,7 @@ func (m *Memory) GetUsers() ([]*User, error) {
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]
if !ok {
return nil, ErrInvalidSession
@ -91,7 +92,7 @@ func (m *Memory) GetSession(token string) (*Session, error) {
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]
if !ok {
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/auth"
"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/util"
"github.com/gofiber/fiber/v2"
@ -17,7 +18,7 @@ import (
"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 {
token := c.Cookies("session-token")
user, _ := auth.UserForSession(as, token)
@ -44,7 +45,7 @@ func main() {
// Serve content statically from "./public", mounted on the "/public/" route
app.Static("/public/", "./public")
authService := auth.New(config.AuthServiceHost)
authService := auth.NewDefaultService(config.AuthServiceHost)
app.Use(UserMiddleware(authService))
// Templates & Renderer
@ -157,7 +158,7 @@ func main() {
})
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 {
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