Merge remote-tracking branch 'origin/master' into webui
This commit is contained in:
@@ -43,10 +43,12 @@ func main() {
|
||||
|
||||
// Controllers
|
||||
userController := controllers.NewUserController(sqlite.UserRepository, sqlite.SessionRepository)
|
||||
groupController := controllers.NewGroupController(sqlite.GroupRepository, sqlite.UserRepository, sqlite.LightRepository)
|
||||
|
||||
// Router
|
||||
router := mux.NewRouter()
|
||||
router.Use(middlewares.Session(sqlite.SessionRepository))
|
||||
groupController.Mount(router, "/api/group/")
|
||||
userController.Mount(router, "/api/user/")
|
||||
|
||||
// Background Tasks
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.aiterp.net/lucifer/lucifer/internal/respond"
|
||||
"git.aiterp.net/lucifer/lucifer/models"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// The GroupController is a controller for all user inports.
|
||||
type GroupController struct {
|
||||
groups models.GroupRepository
|
||||
users models.UserRepository
|
||||
lights models.LightRepository
|
||||
}
|
||||
|
||||
// getGroups (`GET /:id`): Get user by id
|
||||
func (c *GroupController) getGroups(w http.ResponseWriter, r *http.Request) {
|
||||
session := models.SessionFromContext(r.Context())
|
||||
if session == nil {
|
||||
respond.Error(w, http.StatusForbidden, "permission_denied", "You must log in")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.users.FindByID(r.Context(), session.UserID)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusForbidden, "permission_denied", "You must log in")
|
||||
return
|
||||
}
|
||||
|
||||
groups, err := c.groups.ListByUser(r.Context(), user)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
log.Printf("Getting groups for user %s (%d) failed: %s", user.Name, user.ID, err)
|
||||
respond.Error(w, http.StatusInternalServerError, "internal_error", "Failed to get groups.")
|
||||
return
|
||||
}
|
||||
|
||||
respond.Data(w, groups)
|
||||
}
|
||||
|
||||
// getGroup (`GET /:id`): Get user by id
|
||||
func (c *GroupController) getGroup(w http.ResponseWriter, r *http.Request) {
|
||||
session := models.SessionFromContext(r.Context())
|
||||
if session == nil {
|
||||
respond.Error(w, http.StatusForbidden, "permission_denied", "You must log in")
|
||||
return
|
||||
}
|
||||
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := strconv.ParseInt(idStr, 10, 32)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid_id", "The id"+idStr+"is not valid.")
|
||||
return
|
||||
}
|
||||
|
||||
group, err := c.groups.FindByID(r.Context(), int(id))
|
||||
if err != nil || !group.Permission(session.UserID).Read {
|
||||
respond.Error(w, http.StatusNotFound, "not_found", "The group cannot be found or you are not authorized to view it.")
|
||||
return
|
||||
}
|
||||
|
||||
respond.Data(w, group)
|
||||
}
|
||||
|
||||
// getGroupLights (`GET /:id/light/`): Get user by id
|
||||
func (c *GroupController) getGroupLights(w http.ResponseWriter, r *http.Request) {
|
||||
session := models.SessionFromContext(r.Context())
|
||||
if session == nil {
|
||||
respond.Error(w, http.StatusForbidden, "permission_denied", "You must log in")
|
||||
return
|
||||
}
|
||||
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := strconv.ParseInt(idStr, 10, 32)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid_id", "The id "+idStr+" is not valid.")
|
||||
return
|
||||
}
|
||||
|
||||
group, err := c.groups.FindByID(r.Context(), int(id))
|
||||
if err != nil || !group.Permission(session.UserID).Read {
|
||||
respond.Error(w, http.StatusNotFound, "group_not_found", "The group cannot be found or you are not authorized to view it.")
|
||||
return
|
||||
}
|
||||
|
||||
lights, err := c.lights.ListByGroup(r.Context(), group)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
log.Printf("Getting lights for group %s (%d) failed: %s", group.Name, group.ID, err)
|
||||
respond.Error(w, http.StatusInternalServerError, "internal_error", "Failed to get groups.")
|
||||
return
|
||||
}
|
||||
|
||||
respond.Data(w, lights)
|
||||
}
|
||||
|
||||
// getGroupLight (`GET /:group_id/light/:id`): Get user by id
|
||||
func (c *GroupController) getGroupLight(w http.ResponseWriter, r *http.Request) {
|
||||
session := models.SessionFromContext(r.Context())
|
||||
if session == nil {
|
||||
respond.Error(w, http.StatusForbidden, "permission_denied", "You must log in")
|
||||
return
|
||||
}
|
||||
|
||||
groupIDStr := mux.Vars(r)["group_id"]
|
||||
groupID, err := strconv.ParseInt(groupIDStr, 10, 32)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid_id", "The group id "+groupIDStr+" is not valid.")
|
||||
return
|
||||
}
|
||||
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := strconv.ParseInt(idStr, 10, 32)
|
||||
if err != nil {
|
||||
respond.Error(w, http.StatusBadRequest, "invalid_id", "The light id "+idStr+" is not valid.")
|
||||
return
|
||||
}
|
||||
|
||||
group, err := c.groups.FindByID(r.Context(), int(groupID))
|
||||
if err != nil || !group.Permission(session.UserID).Read {
|
||||
respond.Error(w, http.StatusNotFound, "group_not_found", "The group cannot be found or you are not authorized to view it.")
|
||||
return
|
||||
}
|
||||
|
||||
light, err := c.lights.FindByID(r.Context(), int(id))
|
||||
if err != nil || light.GroupID != group.ID {
|
||||
fmt.Println(light, id)
|
||||
respond.Error(w, http.StatusNotFound, "light_not_found", "The light cannot be found in this group.")
|
||||
return
|
||||
}
|
||||
|
||||
respond.Data(w, light)
|
||||
}
|
||||
|
||||
// Mount mounts the controller
|
||||
func (c *GroupController) Mount(router *mux.Router, prefix string) {
|
||||
sub := router.PathPrefix(prefix).Subrouter()
|
||||
|
||||
sub.HandleFunc("/", c.getGroups).Methods("GET")
|
||||
sub.HandleFunc("/{id}", c.getGroup).Methods("GET")
|
||||
sub.HandleFunc("/{id}/light/", c.getGroupLights).Methods("GET")
|
||||
sub.HandleFunc("/{group_id}/light/{id}", c.getGroupLight).Methods("GET")
|
||||
}
|
||||
|
||||
// NewGroupController creates a new GroupController.
|
||||
func NewGroupController(groups models.GroupRepository, users models.UserRepository, lights models.LightRepository) *GroupController {
|
||||
return &GroupController{groups: groups, users: users, lights: lights}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func (c *UserController) login(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err := c.sessions.Insert(r.Context(), session); err != nil {
|
||||
log.Printf("Session create for user %s (%d) failed: %s", user.Name, user.ID, err)
|
||||
respond.Error(w, http.StatusInternalServerError, "session_failure", "Failed to open session.")
|
||||
respond.Error(w, http.StatusInternalServerError, "internal_error", "Failed to open session.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -15,14 +15,36 @@ var GroupRepository = &groupRepository{}
|
||||
|
||||
func (r *groupRepository) FindByID(ctx context.Context, id int) (models.Group, error) {
|
||||
group := models.Group{}
|
||||
err := db.GetContext(ctx, &group, "SELECT * FROM group WHERE id=?", id)
|
||||
err := db.GetContext(ctx, &group, "SELECT * FROM 'group' WHERE id=?", id)
|
||||
if err != nil {
|
||||
return models.Group{}, err
|
||||
}
|
||||
|
||||
err = db.SelectContext(ctx, &group.Permissions, "SELECT * FROM group_permission WHERE group_id=?", group.ID)
|
||||
if err != nil {
|
||||
return models.Group{}, err
|
||||
}
|
||||
|
||||
return group, err
|
||||
}
|
||||
|
||||
func (r *groupRepository) FindByLight(ctx context.Context, light models.Light) (models.Group, error) {
|
||||
const query = `
|
||||
SELECT g.id, g.name FROM 'light' l
|
||||
JOIN 'group' g ON g.id = l.group_id
|
||||
WHERE l.id = ?
|
||||
`
|
||||
|
||||
group := models.Group{}
|
||||
err := db.GetContext(ctx, &group, "SELECT g.id, g.name FROM `light` l JOIN `group` g ON g.id = l.group_id WHERE l.id = ?", light.ID)
|
||||
err := db.GetContext(ctx, &group, query, light.ID)
|
||||
if err != nil {
|
||||
return models.Group{}, err
|
||||
}
|
||||
|
||||
err = db.SelectContext(ctx, &group.Permissions, "SELECT * FROM group_permission WHERE group_id=?", group.ID)
|
||||
if err != nil {
|
||||
return models.Group{}, err
|
||||
}
|
||||
|
||||
return group, err
|
||||
}
|
||||
@@ -39,15 +61,46 @@ func (r *groupRepository) List(ctx context.Context) ([]models.Group, error) {
|
||||
group := &groups[i]
|
||||
|
||||
eg.Go(func() error {
|
||||
return db.SelectContext(egCtx, &group.Permissions, "SELECT * FROM group_permissions WHERE group_id=?", group.ID)
|
||||
return db.SelectContext(egCtx, &group.Permissions, "SELECT * FROM group_permission WHERE group_id=?", group.ID)
|
||||
})
|
||||
}
|
||||
|
||||
return groups, eg.Wait()
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (r *groupRepository) ListByUser(ctx context.Context, user models.User) ([]models.Group, error) {
|
||||
panic("not implemented")
|
||||
const query = `
|
||||
SELECT g.id, g.name FROM group_permission AS p
|
||||
JOIN 'group' AS g ON p.group_id=g.id
|
||||
WHERE p.user_id=? AND p.read=true;
|
||||
`
|
||||
|
||||
groups := make([]models.Group, 0, 16)
|
||||
err := db.SelectContext(ctx, &groups, query, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eg, egCtx := errgroup.WithContext(ctx)
|
||||
for i := range groups {
|
||||
group := &groups[i]
|
||||
|
||||
eg.Go(func() error {
|
||||
return db.SelectContext(egCtx, &group.Permissions, "SELECT * FROM group_permission WHERE group_id=?", group.ID)
|
||||
})
|
||||
}
|
||||
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (r *groupRepository) Insert(ctx context.Context, group models.Group) (models.Group, error) {
|
||||
|
||||
+17
-2
@@ -10,10 +10,25 @@ type Group struct {
|
||||
Permissions []GroupPermission `json:"permissions" db:"-"`
|
||||
}
|
||||
|
||||
// Permission gets the permissions for the user with id `userID` or returns
|
||||
// a blank permission if none is found.
|
||||
func (group *Group) Permission(userID int) GroupPermission {
|
||||
for _, permission := range group.Permissions {
|
||||
if permission.UserID == userID {
|
||||
return permission
|
||||
}
|
||||
}
|
||||
|
||||
return GroupPermission{
|
||||
GroupID: group.ID,
|
||||
UserID: userID,
|
||||
}
|
||||
}
|
||||
|
||||
// A GroupPermission is a permission for a user in a group.
|
||||
type GroupPermission struct {
|
||||
GroupID int `json:"group_id" db:"group_id"`
|
||||
UserID int `json:"user_id" db:"user_id"`
|
||||
GroupID int `json:"-" db:"group_id"`
|
||||
UserID int `json:"userId" db:"user_id"`
|
||||
Read bool `json:"read" db:"read"`
|
||||
Write bool `json:"write" db:"write"`
|
||||
Create bool `json:"create" db:"create"`
|
||||
|
||||
Reference in New Issue
Block a user