forked from phc/website
Working auth system
This commit is contained in:
@@ -10,3 +10,6 @@ CHAT_URL=https://chat.phc.dm.unipi.it
|
|||||||
EMAIL=macchinisti@lists.dm.unipi.it
|
EMAIL=macchinisti@lists.dm.unipi.it
|
||||||
|
|
||||||
USER_PAGES_BASE_URL=https://poisson.phc.dm.unipi.it/~
|
USER_PAGES_BASE_URL=https://poisson.phc.dm.unipi.it/~
|
||||||
|
|
||||||
|
# AuthService
|
||||||
|
AUTH_SERVICE_HOST=:memory:
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ node_modules/
|
|||||||
|
|
||||||
# Don't version generated files
|
# Don't version generated files
|
||||||
public/js/*.min.js
|
public/js/*.min.js
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
phc-website-server
|
||||||
|
|||||||
@@ -91,3 +91,9 @@ $ fd -e js | entr make js
|
|||||||
Rappresentano link ad altri servizi forniti, è comodo impostarli per testare tutto in locale su varie porte (e poi in produzione i vari url diventano link a sotto-domini del sito principale).
|
Rappresentano link ad altri servizi forniti, è comodo impostarli per testare tutto in locale su varie porte (e poi in produzione i vari url diventano link a sotto-domini del sito principale).
|
||||||
|
|
||||||
Per ora ci sono `GIT_URL`, `CHAT_URL` e `USER_PAGES_BASE_URL`.
|
Per ora ci sono `GIT_URL`, `CHAT_URL` e `USER_PAGES_BASE_URL`.
|
||||||
|
|
||||||
|
## Altri Servizi
|
||||||
|
|
||||||
|
Questo servizio dipende dal servizio di autenticazione per permettere agli utenti di autenticarsi usando vari meccanismi.
|
||||||
|
|
||||||
|
Il servizio di autenticazione di default girerà su `localhost:3535` ed è documentato [sulla repo auth-service](https://git.phc.dm.unipi.it/phc/auth-service/)
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// AuthService rappresenta un servizio di autenticazione
|
|
||||||
// type AuthService interface {
|
|
||||||
// GetUsers() []User
|
|
||||||
|
|
||||||
// GetUser(username string) User
|
|
||||||
|
|
||||||
// // LoginUser if successful returns the token for this user that will be stored in an HTTP cookie.
|
|
||||||
// LoginUser(username, password string) (string, error)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// LdapService ...
|
|
||||||
type LdapService struct {
|
|
||||||
URL string
|
|
||||||
}
|
|
||||||
|
|
||||||
// FakeService ...
|
|
||||||
type FakeService struct {
|
|
||||||
URL string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAuthenticationService crea un nuovo servizio di autenticazione e controlla se è attivo
|
|
||||||
// func NewAuthenticationService(url string) (*LdapService, error) {
|
|
||||||
// service := new(LdapService)
|
|
||||||
// service.URL = url
|
|
||||||
|
|
||||||
// res, err := service.Get("status")
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// status, _ := ioutil.ReadAll(res.Body)
|
|
||||||
|
|
||||||
// if string(status) != "true" {
|
|
||||||
// log.Fatalf("Authentication service isn't online, status: '%s'", status)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return service, nil
|
|
||||||
// }
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
type User interface {
|
||||||
|
GetUsername() string
|
||||||
|
GetName() string
|
||||||
|
GetSurname() string
|
||||||
|
GetFullName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session interface {
|
||||||
|
GetUsername() string
|
||||||
|
GetToken() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthenticatorService interface {
|
||||||
|
GetUser(username string) (User, error)
|
||||||
|
GetUsers() ([]User, error)
|
||||||
|
GetSession(token string) (Session, error)
|
||||||
|
Login(username, password string) (Session, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserForSession(as AuthenticatorService, token string) (User, error) {
|
||||||
|
session, err := as.GetSession(token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := as.GetUser(session.GetUsername())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(host string) AuthenticatorService {
|
||||||
|
if host == ":memory:" {
|
||||||
|
return exampleMemoryUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
return &LDAPAuthService{host}
|
||||||
|
}
|
||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LDAPUser struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
NumericId int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
Gecos string `json:"gecos"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u LDAPUser) GetUsername() string {
|
||||||
|
return u.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u LDAPUser) GetName() string {
|
||||||
|
return u.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u LDAPUser) GetSurname() string {
|
||||||
|
return u.Surname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u LDAPUser) GetFullName() string {
|
||||||
|
return u.Gecos
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimpleSession struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
CreatedOn time.Time `json:"createdOn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SimpleSession) GetUsername() string {
|
||||||
|
return s.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SimpleSession) GetToken() string {
|
||||||
|
return s.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type LDAPAuthService struct {
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthService) doGetRequest(url string, response interface{}) error {
|
||||||
|
req, err := http.NewRequest(
|
||||||
|
"GET", path.Join(a.Host, "ldap", url), bytes.NewBuffer([]byte("")),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.NewDecoder(res.Body).Decode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthService) doPostRequest(url string, request interface{}, response interface{}) error {
|
||||||
|
jsonStr, err := json.Marshal(request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", path.Join(a.Host, "ldap", url), bytes.NewBuffer(jsonStr))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.NewDecoder(res.Body).Decode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthService) GetUser(username string) (User, error) {
|
||||||
|
var user LDAPUser
|
||||||
|
if err := a.doGetRequest(fmt.Sprintf("/user/%s", username), &user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthService) GetUsers() ([]User, error) {
|
||||||
|
ldapUsers := []*LDAPUser{}
|
||||||
|
if err := a.doGetRequest(fmt.Sprintf("/users"), &ldapUsers); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := make([]User, len(ldapUsers))
|
||||||
|
for i, u := range ldapUsers {
|
||||||
|
users[i] = u
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthService) GetSession(token string) (Session, error) {
|
||||||
|
var response SimpleSession
|
||||||
|
if err := a.doGetRequest(fmt.Sprintf("/session/%s", token), &response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthService) Login(username, password string) (Session, error) {
|
||||||
|
body := map[string]interface{}{
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
}
|
||||||
|
|
||||||
|
var response SimpleSession
|
||||||
|
if err := a.doPostRequest(fmt.Sprintf("/login"), body, &response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.phc.dm.unipi.it/phc/website/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var exampleMemoryUsers = &Memory{
|
||||||
|
Users: map[string]*MemoryUser{
|
||||||
|
"aziis98": {
|
||||||
|
Username: "aziis98",
|
||||||
|
Name: "Antonio",
|
||||||
|
Surname: "De Lucreziis",
|
||||||
|
Password: "123",
|
||||||
|
},
|
||||||
|
"bachoseven": {
|
||||||
|
Username: "bachoseven",
|
||||||
|
Name: "Francesco",
|
||||||
|
Surname: "Minnocci",
|
||||||
|
Password: "234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sessions: map[string]*MemorySession{},
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryUser struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
|
||||||
|
Password string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MemoryUser) GetUsername() string {
|
||||||
|
return u.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MemoryUser) GetName() string {
|
||||||
|
return u.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MemoryUser) GetSurname() string {
|
||||||
|
return u.Surname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *MemoryUser) GetFullName() string {
|
||||||
|
return u.Name + " " + u.Surname
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemorySession struct {
|
||||||
|
Username string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemorySession) GetUsername() string {
|
||||||
|
return s.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MemorySession) GetToken() string {
|
||||||
|
return s.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type Memory struct {
|
||||||
|
Users map[string]*MemoryUser
|
||||||
|
Sessions map[string]*MemorySession
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Memory) GetUser(username string) (User, error) {
|
||||||
|
user, ok := m.Users[username]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`no user with that username`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Memory) GetUsers() ([]User, error) {
|
||||||
|
users := make([]User, len(m.Users))
|
||||||
|
i := 0
|
||||||
|
for _, u := range m.Users {
|
||||||
|
users[i] = u
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Memory) GetSession(token string) (Session, error) {
|
||||||
|
session, ok := m.Sessions[token]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`invalid session token`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Memory) Login(username string, password string) (Session, error) {
|
||||||
|
user, err := m.GetUser(username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
memUser := user.(*MemoryUser)
|
||||||
|
|
||||||
|
if memUser.Password != password {
|
||||||
|
return nil, fmt.Errorf(`invalid credentials`)
|
||||||
|
}
|
||||||
|
|
||||||
|
session := &MemorySession{username, util.GenerateRandomString(15)}
|
||||||
|
m.Sessions[session.Token] = session
|
||||||
|
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
+10
-1
@@ -18,6 +18,8 @@ var Email string
|
|||||||
|
|
||||||
var UserPagesBaseUrl string
|
var UserPagesBaseUrl string
|
||||||
|
|
||||||
|
var AuthServiceHost string
|
||||||
|
|
||||||
func loadEnv(target *string, name, defaultValue string) {
|
func loadEnv(target *string, name, defaultValue string) {
|
||||||
value := os.Getenv(name)
|
value := os.Getenv(name)
|
||||||
if len(strings.TrimSpace(value)) == 0 {
|
if len(strings.TrimSpace(value)) == 0 {
|
||||||
@@ -30,17 +32,22 @@ func loadEnv(target *string, name, defaultValue string) {
|
|||||||
|
|
||||||
func Load() {
|
func Load() {
|
||||||
godotenv.Load()
|
godotenv.Load()
|
||||||
|
|
||||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
|
||||||
|
// Production
|
||||||
loadEnv(&Mode, "MODE", "production")
|
loadEnv(&Mode, "MODE", "production")
|
||||||
loadEnv(&Host, "HOST", "localhost:8080")
|
loadEnv(&Host, "HOST", "localhost:8080")
|
||||||
|
|
||||||
|
// Services
|
||||||
loadEnv(&GitUrl, "GIT_URL", "https://git.example.org")
|
loadEnv(&GitUrl, "GIT_URL", "https://git.example.org")
|
||||||
loadEnv(&ChatUrl, "CHAT_URL", "https://chat.example.org")
|
loadEnv(&ChatUrl, "CHAT_URL", "https://chat.example.org")
|
||||||
loadEnv(&Email, "EMAIL", "mail@example.org")
|
loadEnv(&Email, "EMAIL", "mail@example.org")
|
||||||
|
|
||||||
|
// Poisson
|
||||||
loadEnv(&UserPagesBaseUrl, "USER_PAGES_BASE_URL", "https://poisson.phc.dm.unipi.it/~")
|
loadEnv(&UserPagesBaseUrl, "USER_PAGES_BASE_URL", "https://poisson.phc.dm.unipi.it/~")
|
||||||
|
|
||||||
|
// AuthService
|
||||||
|
loadEnv(&AuthServiceHost, "AUTH_SERVICE_HOST", "http://localhost:3535")
|
||||||
}
|
}
|
||||||
|
|
||||||
func Object() util.H {
|
func Object() util.H {
|
||||||
@@ -53,5 +60,7 @@ func Object() util.H {
|
|||||||
"Email": Email,
|
"Email": Email,
|
||||||
|
|
||||||
"UserPagesBaseUrl": UserPagesBaseUrl,
|
"UserPagesBaseUrl": UserPagesBaseUrl,
|
||||||
|
|
||||||
|
"AuthServiceHost": AuthServiceHost,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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/config"
|
"git.phc.dm.unipi.it/phc/website/config"
|
||||||
"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"
|
||||||
@@ -13,6 +16,15 @@ import (
|
|||||||
"github.com/gofiber/redirect/v2"
|
"github.com/gofiber/redirect/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func UserMiddleware(as auth.AuthenticatorService) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
token := c.Cookies("session-token")
|
||||||
|
user, _ := auth.UserForSession(as, token)
|
||||||
|
c.Locals("user", user)
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.Load()
|
config.Load()
|
||||||
|
|
||||||
@@ -29,6 +41,9 @@ func main() {
|
|||||||
// Static content
|
// Static content
|
||||||
app.Static("/public/", "./public")
|
app.Static("/public/", "./public")
|
||||||
|
|
||||||
|
authService := auth.New(config.AuthServiceHost)
|
||||||
|
app.Use(UserMiddleware(authService))
|
||||||
|
|
||||||
// Templates & Renderer
|
// Templates & Renderer
|
||||||
renderer := templates.NewRenderer(
|
renderer := templates.NewRenderer(
|
||||||
"./views/",
|
"./views/",
|
||||||
@@ -51,7 +66,9 @@ func main() {
|
|||||||
localView := view
|
localView := view
|
||||||
app.Get(route, func(c *fiber.Ctx) error {
|
app.Get(route, func(c *fiber.Ctx) error {
|
||||||
c.Type("html")
|
c.Type("html")
|
||||||
return renderer.Render(c, localView, util.H{})
|
return renderer.Render(c, localView, util.H{
|
||||||
|
"User": c.Locals("user"),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +89,7 @@ func main() {
|
|||||||
|
|
||||||
c.Type("html")
|
c.Type("html")
|
||||||
return renderer.Render(c, "storia.html", util.H{
|
return renderer.Render(c, "storia.html", util.H{
|
||||||
|
"User": c.Locals("user"),
|
||||||
"Storia": storia,
|
"Storia": storia,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -81,6 +99,7 @@ func main() {
|
|||||||
|
|
||||||
c.Type("html")
|
c.Type("html")
|
||||||
return renderer.Render(c, "appunti.html", util.H{
|
return renderer.Render(c, "appunti.html", util.H{
|
||||||
|
"User": c.Locals("user"),
|
||||||
"Query": searchQuery,
|
"Query": searchQuery,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -93,10 +112,50 @@ func main() {
|
|||||||
|
|
||||||
c.Type("html")
|
c.Type("html")
|
||||||
return renderer.Render(c, "news.html", util.H{
|
return renderer.Render(c, "news.html", util.H{
|
||||||
|
"User": c.Locals("user"),
|
||||||
"Articles": articles,
|
"Articles": articles,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.Post("/login", func(c *fiber.Ctx) error {
|
||||||
|
var loginForm struct {
|
||||||
|
Provider string `form:"provider"`
|
||||||
|
Username string `form:"username"`
|
||||||
|
Password string `form:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&loginForm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := authService.Login(loginForm.Username, loginForm.Password)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
inThreeDays := time.Now().Add(3 * 24 * time.Hour)
|
||||||
|
c.Cookie(&fiber.Cookie{
|
||||||
|
Name: "session-token",
|
||||||
|
Path: "/",
|
||||||
|
Value: session.GetToken(),
|
||||||
|
Expires: inThreeDays,
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.Redirect("/profilo")
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Get("/profilo", func(c *fiber.Ctx) error {
|
||||||
|
user, ok := c.Locals("user").(auth.User)
|
||||||
|
if !ok || user == nil {
|
||||||
|
return fmt.Errorf(`no logged in user`)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Type("html")
|
||||||
|
return renderer.Render(c, "profilo.html", util.H{
|
||||||
|
"User": c.Locals("user"),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
app.Get("/news/:article", func(c *fiber.Ctx) error {
|
app.Get("/news/:article", func(c *fiber.Ctx) error {
|
||||||
articleID := c.Params("article")
|
articleID := c.Params("article")
|
||||||
|
|
||||||
@@ -112,6 +171,7 @@ func main() {
|
|||||||
|
|
||||||
c.Type("html")
|
c.Type("html")
|
||||||
return renderer.Render(c, "news-base.html", util.H{
|
return renderer.Render(c, "news-base.html", util.H{
|
||||||
|
"User": c.Locals("user"),
|
||||||
"Article": article,
|
"Article": article,
|
||||||
"ContentHTML": template.HTML(html),
|
"ContentHTML": template.HTML(html),
|
||||||
})
|
})
|
||||||
|
|||||||
+10
-3
@@ -322,13 +322,15 @@ section {
|
|||||||
.card .title {
|
.card .title {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
|
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card .date {
|
.card .date {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: var(--card-date);
|
color: var(--card-date);
|
||||||
|
|
||||||
margin-bottom: 0.75rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card .description {
|
.card .description {
|
||||||
@@ -341,7 +343,7 @@ section {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 0 0.5rem;
|
gap: 0 0.5rem;
|
||||||
|
|
||||||
margin-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags .tag {
|
.tags .tag {
|
||||||
@@ -393,9 +395,10 @@ p,
|
|||||||
ul,
|
ul,
|
||||||
ol,
|
ol,
|
||||||
li {
|
li {
|
||||||
margin: 0.5rem 0;
|
margin: 0;
|
||||||
width: 70ch;
|
width: 70ch;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
line-height: 1.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul,
|
ul,
|
||||||
@@ -832,6 +835,10 @@ form .field-set input {
|
|||||||
|
|
||||||
/* Rendered Markdown */
|
/* Rendered Markdown */
|
||||||
|
|
||||||
|
.news-content p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
.news-content {
|
.news-content {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
package util
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateRandomString(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.URLEncoding.EncodeToString(b)
|
||||||
|
}
|
||||||
+2
-2
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
<h1>
|
||||||
<i class="fas fa-book"></i>
|
<i class="fas fa-book"></i>
|
||||||
Raccolta degli Appunti
|
Raccolta degli Appunti
|
||||||
</h2>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
Questa è la raccolta degli appunti presenti su Poisson. Cerca il titolo della dispensa, il nome e cognome o l'username dell'autore oppure scrivi il nome del corso rispetto a cui filtrare. Altrimenti in cima compariranno gli appunti più "gettonati".
|
Questa è la raccolta degli appunti presenti su Poisson. Cerca il titolo della dispensa, il nome e cognome o l'username dell'autore oppure scrivi il nome del corso rispetto a cui filtrare. Altrimenti in cima compariranno gli appunti più "gettonati".
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
+9
-6
@@ -28,8 +28,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="date">yyyy-mm-dd</div>
|
<div class="date">yyyy-mm-dd</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
much doge, ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
|
<p>
|
||||||
|
much doge, ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -38,8 +39,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="date">yyyy-mm-dd</div>
|
<div class="date">yyyy-mm-dd</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -48,8 +50,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="date">yyyy-mm-dd</div>
|
<div class="date">yyyy-mm-dd</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+20
-8
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
<h1>
|
||||||
<i class="fas fa-link"></i>
|
<i class="fas fa-link"></i>
|
||||||
Link Utili
|
Link Utili
|
||||||
</h2>
|
</h1>
|
||||||
<p class="center">
|
<p class="center">
|
||||||
Questo è un elenco di alcuni indirizzi potenzialmente utili
|
Questo è un elenco di alcuni indirizzi potenzialmente utili
|
||||||
</p>
|
</p>
|
||||||
@@ -19,7 +19,9 @@
|
|||||||
<i class="fas fa-external-link-alt"></i>
|
<i class="fas fa-external-link-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Lista di link relativi alle attività dell'aula studenti del Dipartimento di Matematica
|
<p>
|
||||||
|
Lista di link relativi alle attività dell'aula studenti del Dipartimento di Matematica
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -28,7 +30,9 @@
|
|||||||
<i class="fas fa-external-link-alt"></i>
|
<i class="fas fa-external-link-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Sito per chiedere il recupero/reset delle proprie credenziali Poisson; le matricole possono inserire direttamente le loro credenziali di Alice ed ottenere quelle del loro account su Poisson.
|
<p>
|
||||||
|
Sito per chiedere il recupero/reset delle proprie credenziali Poisson; le matricole possono inserire direttamente le loro credenziali di Alice ed ottenere quelle del loro account su Poisson.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -37,7 +41,9 @@
|
|||||||
<i class="fas fa-external-link-alt"></i>
|
<i class="fas fa-external-link-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Questo sito ti permette di disegnare "a mano" il simbolo che cerchi e trovare il suo corrispondente comando in LaTex.
|
<p>
|
||||||
|
Questo sito ti permette di disegnare "a mano" il simbolo che cerchi e trovare il suo corrispondente comando in LaTex.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -46,7 +52,9 @@
|
|||||||
<i class="fas fa-external-link-alt"></i>
|
<i class="fas fa-external-link-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Un semplice shell script che permette di stampare un file in una stampante del Dipartimento di Matematica da una qualsiasi shell Unix (Linux, MacOS, BSD...). Funziona anche da remoto!
|
<p>
|
||||||
|
Un semplice shell script che permette di stampare un file in una stampante del Dipartimento di Matematica da una qualsiasi shell Unix (Linux, MacOS, BSD...). Funziona anche da remoto!
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -55,7 +63,9 @@
|
|||||||
<i class="fas fa-external-link-alt"></i>
|
<i class="fas fa-external-link-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
Sei stanco del pessimo font di Wikipedia? Finalmente potrai leggere i <a href="https://en.wikipedia.org/wiki/Abstract_nonsense">tuoi</a> <a href="https://en.wikipedia.org/wiki/New_Math">articoli</a> <a href="https://en.wikipedia.org/wiki/%C3%89variste_Galois#Final_days">preferiti</a> di Wikipedia con un typesetting in stile LaTex!
|
<p>
|
||||||
|
Sei stanco del pessimo font di Wikipedia? Finalmente potrai leggere i <a href="https://en.wikipedia.org/wiki/Abstract_nonsense">tuoi</a> <a href="https://en.wikipedia.org/wiki/New_Math">articoli</a> <a href="https://en.wikipedia.org/wiki/%C3%89variste_Galois#Final_days">preferiti</a> di Wikipedia con un typesetting in stile LaTex!
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -64,7 +74,9 @@
|
|||||||
<i class="fas fa-external-link-alt"></i>
|
<i class="fas fa-external-link-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
La homepage del corso di studi all'interno del sito ufficiale del Dipartimento di Matematica
|
<p>
|
||||||
|
La homepage del corso di studi all'interno del sito ufficiale del Dipartimento di Matematica
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+9
-7
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
<h1>
|
||||||
<i class="fas fa-user"></i>
|
<i class="fas fa-user"></i>
|
||||||
Account di Poisson
|
Account di Poisson
|
||||||
</h2>
|
</h1>
|
||||||
<div class="card-list">
|
<div class="card-list">
|
||||||
<form class="card" action="/login" method="POST">
|
<form class="card" action="/login" method="POST">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -15,10 +15,13 @@
|
|||||||
Accedi
|
Accedi
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
Inserisci le tue credenziali di Poisson per accedere.
|
Inserisci le tue credenziali di Poisson per accedere
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="field-set">
|
<div class="field-set">
|
||||||
|
<!-- <label for="login-provider">Provider:</label> -->
|
||||||
|
<input type="hidden" name="provider" id="login-provider" value="poisson-ldap">
|
||||||
|
|
||||||
<label for="login-username">Username:</label>
|
<label for="login-username">Username:</label>
|
||||||
<input type="text" name="username" id="login-username">
|
<input type="text" name="username" id="login-username">
|
||||||
|
|
||||||
@@ -35,11 +38,10 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Ottenere un account</h2>
|
<h2>Ottenere un account</h2>
|
||||||
<p>
|
<p>
|
||||||
Se vuoi ottenere un account compila* il <a href="#">modulo di richiesta</a> e portacelo in PHC o inviacelo via
|
Dal 2022 in nuovi utenti hanno bisogno di compilare un modulo se vogliono ottenere un account<sup>1</sup>, scarica il <a href="#">modulo di richiesta</a> e portacelo in PHC o inviacelo via email all'indirizzo <a href="mailto:{{ .Config.Email }}">{{ .Config.Email }}</a>.
|
||||||
email all'indirizzo <a href="mailto:{{ .Config.Email }}">{{ .Config.Email }}</a>.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
*In realtà il modulo ancora non esiste
|
1: In realtà il modulo ancora non esiste
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@
|
|||||||
<h2>Recupero credenziali</h2>
|
<h2>Recupero credenziali</h2>
|
||||||
<p>
|
<p>
|
||||||
Per il recupero credenziali vieni direttamente al PHC a parlarne con calma con noi altrimenti puoi inviaci una
|
Per il recupero credenziali vieni direttamente al PHC a parlarne con calma con noi altrimenti puoi inviaci una
|
||||||
email all'indirizzo <a href="mailto:{{ .Config.Email }}">{{ .Config.Email }}</a>.
|
email all'indirizzo <a href="mailto:{{ .Config.Email }}">{{ .Config.Email }}</a> e poi recuperare le nuove credenziali sul sito <a href="https://credenziali.phc.dm.unipi.it/">credenziali.phc.dm.unipi.it</a>.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
{{end}}
|
{{end}}
|
||||||
+8
-4
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
{{define "body"}}
|
{{define "body"}}
|
||||||
<section>
|
<section>
|
||||||
<h2>
|
<h1>
|
||||||
<i class="far fa-newspaper"></i>
|
<i class="far fa-newspaper"></i>
|
||||||
Notizie Importanti
|
Notizie Importanti
|
||||||
</h2>
|
</h1>
|
||||||
<div class="card-list">
|
<div class="card-list">
|
||||||
{{ range .Articles }}
|
{{ range .Articles }}
|
||||||
{{ if .HasTag "important" }}
|
{{ if .HasTag "important" }}
|
||||||
@@ -18,7 +18,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
|
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
|
||||||
<div class="description">{{ .Description }}</div>
|
<div class="description">
|
||||||
|
<p>{{ .Description }}</p>
|
||||||
|
</div>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
{{ range .Tags }}
|
{{ range .Tags }}
|
||||||
<span class="tag">{{ . }}</span>
|
<span class="tag">{{ . }}</span>
|
||||||
@@ -45,7 +47,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
|
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
|
||||||
<div class="description">{{ .Description }}</div>
|
<div class="description">
|
||||||
|
<p>{{ .Description }}</p>
|
||||||
|
</div>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
{{ range .Tags }}
|
{{ range .Tags }}
|
||||||
<span class="tag">{{ . }}</span>
|
<span class="tag">{{ . }}</span>
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{if .User}}
|
{{if .User}}
|
||||||
<div class="nav-item">
|
<div class="nav-item">
|
||||||
<a class="nav-element" href="/profile">@{{ .User }}</a>
|
<a class="nav-element" href="/profilo">@{{ .User.Username }}</a>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="nav-item">
|
<div class="nav-item">
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{{template "base" .}}
|
||||||
|
|
||||||
|
{{define "title"}}Profilo @{{ .User.Username }} • PHC{{end}}
|
||||||
|
|
||||||
|
{{define "body"}}
|
||||||
|
<section>
|
||||||
|
<h1>Profilo di @{{ .User.Username }}</h1>
|
||||||
|
<h2>Impostazioni</h2>
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae earum amet delectus cumque obcaecati minus quos aliquid fugiat reprehenderit voluptatum?
|
||||||
|
</p>
|
||||||
|
<h2>Recupero Credenziali Poisson</h2>
|
||||||
|
<p>
|
||||||
|
Per il recupero credenziali vieni direttamente al PHC a parlarne con calma con noi altrimenti puoi inviaci una email all'indirizzo <a href="mailto:{{ .Config.Email }}">{{ .Config.Email }}</a> e poi recuperare le nuove credenziali sul sito <a href="https://credenziali.phc.dm.unipi.it/">credenziali.phc.dm.unipi.it</a>.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
{{end}}
|
||||||
+2
-2
@@ -5,10 +5,10 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.js"></script>
|
||||||
<script src="/public/js/utenti.min.js"></script>
|
<script src="/public/js/utenti.min.js"></script>
|
||||||
<section x-data="utenti">
|
<section x-data="utenti">
|
||||||
<h2>
|
<h1>
|
||||||
<i class="fas fa-users"></i>
|
<i class="fas fa-users"></i>
|
||||||
Lista degli Utenti
|
Lista degli Utenti
|
||||||
</h2>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
Questa è la lista di tutti gli utenti con un account su Poisson. Scrivi nome, cognome o
|
Questa è la lista di tutti gli utenti con un account su Poisson. Scrivi nome, cognome o
|
||||||
username di un utente per filtrare la lista in tempo reale. Altrimenti di base in cima
|
username di un utente per filtrare la lista in tempo reale. Altrimenti di base in cima
|
||||||
|
|||||||
Reference in New Issue
Block a user