cc-backend/internal/auth-v2/auth.go

140 lines
3.4 KiB
Go
Raw Normal View History

2022-07-07 12:11:49 +02:00
package authv2
import (
2022-07-07 12:48:04 +02:00
"database/sql"
2022-07-07 12:11:49 +02:00
"encoding/json"
"net/http"
2022-07-07 12:48:04 +02:00
"time"
2022-07-07 12:11:49 +02:00
"github.com/ClusterCockpit/cc-backend/pkg/log"
sq "github.com/Masterminds/squirrel"
"github.com/gorilla/sessions"
"github.com/jmoiron/sqlx"
)
const (
RoleAdmin string = "admin"
RoleApi string = "api"
RoleUser string = "user"
)
const (
AuthViaLocalPassword int8 = 0
AuthViaLDAP int8 = 1
AuthViaToken int8 = 2
)
type User struct {
Username string `json:"username"`
Password string `json:"-"`
Name string `json:"name"`
Roles []string `json:"roles"`
AuthSource int8 `json:"via"`
Email string `json:"email"`
2022-07-07 12:48:04 +02:00
Expiration time.Time
2022-07-07 12:11:49 +02:00
}
func (u *User) HasRole(role string) bool {
for _, r := range u.Roles {
if r == role {
return true
}
}
return false
}
type Authenticator interface {
Init(auth *Authentication, config json.RawMessage) error
CanLogin(user *User, rw http.ResponseWriter, r *http.Request) bool
2022-07-07 12:48:04 +02:00
Login(user *User, password string, rw http.ResponseWriter, r *http.Request) (*User, error)
2022-07-07 12:11:49 +02:00
Auth(rw http.ResponseWriter, r *http.Request) (*User, error)
}
type ContextKey string
const ContextUserKey ContextKey = "user"
type Authentication struct {
db *sqlx.DB
sessionStore *sessions.CookieStore
authenticators []Authenticator
}
func Init(db *sqlx.DB) (*Authentication, error) {
auth := &Authentication{}
auth.db = db
_, err := db.Exec(`
CREATE TABLE IF NOT EXISTS user (
username varchar(255) PRIMARY KEY NOT NULL,
password varchar(255) DEFAULT NULL,
ldap tinyint NOT NULL DEFAULT 0, /* col called "ldap" for historic reasons, fills the "AuthSource" */
name varchar(255) DEFAULT NULL,
roles varchar(255) NOT NULL DEFAULT "[]",
email varchar(255) DEFAULT NULL);`)
if err != nil {
return nil, err
}
return auth, nil
}
2022-07-07 12:48:04 +02:00
func (auth *Authentication) GetUser(username string) (*User, error) {
user := &User{Username: username}
var hashedPassword, name, rawRoles, email sql.NullString
if err := sq.Select("password", "ldap", "name", "roles", "email").From("user").
Where("user.username = ?", username).RunWith(auth.db).
QueryRow().Scan(&hashedPassword, &user.AuthSource, &name, &rawRoles, &email); err != nil {
return nil, err
}
user.Password = hashedPassword.String
user.Name = name.String
user.Email = email.String
if rawRoles.Valid {
if err := json.Unmarshal([]byte(rawRoles.String), &user.Roles); err != nil {
return nil, err
}
}
return user, nil
}
func (auth *Authentication) AddUser(user *User) error {
2022-07-07 12:11:49 +02:00
rolesJson, _ := json.Marshal(user.Roles)
cols := []string{"username", "password", "roles"}
vals := []interface{}{user.Username, user.Password, string(rolesJson)}
if user.Name != "" {
cols = append(cols, "name")
vals = append(vals, user.Name)
}
if user.Email != "" {
cols = append(cols, "email")
vals = append(vals, user.Email)
}
if _, err := sq.Insert("user").Columns(cols...).Values(vals...).RunWith(auth.db).Exec(); err != nil {
return err
}
log.Infof("new user %#v created (roles: %s)", user.Username, rolesJson)
return nil
}
func (auth *Authentication) AuthViaSession(rw http.ResponseWriter, r *http.Request) (*User, error) {
session, err := auth.sessionStore.Get(r, "session")
if err != nil {
return nil, err
}
if session.IsNew {
return nil, nil
}
username, _ := session.Values["username"].(string)
roles, _ := session.Values["roles"].([]string)
return &User{
Username: username,
Roles: roles,
}, nil
}