Small auth module refactor
This commit is contained in:
+22
-42
@@ -1,54 +1,32 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
// Session represents a session returned from AuthenticatorService
|
||||
type Session struct {
|
||||
Username string `json:"username"`
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
+15
-9
@@ -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
|
||||
}
|
||||
|
||||
+13
-12
@@ -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"`
|
||||
}
|
||||
Reference in New Issue
Block a user