Compare commits
1 Commits
cdbdea250d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0f8b48869 |
@@ -71,11 +71,13 @@ type app struct {
|
||||
}
|
||||
|
||||
type accessScope struct {
|
||||
IsAdmin bool
|
||||
CanManage bool
|
||||
CanAuth bool
|
||||
DeptID string
|
||||
DeptIDs []string
|
||||
IsAdmin bool
|
||||
CanManage bool
|
||||
CanManageAll bool
|
||||
CanAuth bool
|
||||
CanViewAll bool
|
||||
DeptID string
|
||||
DeptIDs []string
|
||||
}
|
||||
|
||||
type sectionOut struct {
|
||||
@@ -638,7 +640,9 @@ func (a *app) handleAccessMe(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"is_admin": scope.IsAdmin,
|
||||
"can_manage_department": scope.CanManage,
|
||||
"can_manage_all": scope.CanManageAll,
|
||||
"can_auth_telegram": scope.CanAuth,
|
||||
"can_view_all": scope.CanViewAll,
|
||||
"department_id": nullableString(scope.DeptID),
|
||||
"department_ids": scope.departmentIDs(),
|
||||
})
|
||||
@@ -667,7 +671,7 @@ func (a *app) listSections(ctx context.Context, w http.ResponseWriter, r *http.R
|
||||
|
||||
args := []any{vertical}
|
||||
deptFilter := ""
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
}
|
||||
|
||||
@@ -730,11 +734,12 @@ func (a *app) createSection(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
return
|
||||
}
|
||||
var payload struct {
|
||||
Vertical string `json:"vertical"`
|
||||
Slug string `json:"slug"`
|
||||
Title string `json:"title"`
|
||||
Emoji *string `json:"emoji"`
|
||||
Description *string `json:"description"`
|
||||
Vertical string `json:"vertical"`
|
||||
DepartmentID *string `json:"department_id"`
|
||||
Slug string `json:"slug"`
|
||||
Title string `json:"title"`
|
||||
Emoji *string `json:"emoji"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
if !readBody(w, r, &payload) {
|
||||
return
|
||||
@@ -746,7 +751,11 @@ func (a *app) createSection(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
writeError(w, http.StatusBadRequest, "vertical, slug and title are required")
|
||||
return
|
||||
}
|
||||
dept := nullableString(scope.primaryDepartmentID())
|
||||
deptID := scope.primaryDepartmentID()
|
||||
if scope.CanManageAll && payload.DepartmentID != nil {
|
||||
deptID = strings.TrimSpace(*payload.DepartmentID)
|
||||
}
|
||||
dept := nullableString(deptID)
|
||||
row := a.db.QueryRow(ctx, `
|
||||
INSERT INTO sections (vertical, department_id, slug, title, emoji, description)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
@@ -837,7 +846,7 @@ func (a *app) updateSection(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
}
|
||||
args = append(args, vertical, slug)
|
||||
where := fmt.Sprintf("vertical = $%d AND slug = $%d", len(args)-1, len(args))
|
||||
if !scope.IsAdmin {
|
||||
if !scope.CanManageAll {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "department_id")
|
||||
where += deptFilter
|
||||
@@ -865,7 +874,7 @@ func (a *app) deleteSection(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
section, err := a.findSection(ctx, vertical, slug, scope)
|
||||
section, err := a.findSection(ctx, vertical, slug, scope.forManageLookup())
|
||||
if err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
@@ -889,7 +898,7 @@ func (a *app) deleteSection(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
func (a *app) findSection(ctx context.Context, vertical, slug string, scope accessScope) (sectionOut, error) {
|
||||
args := []any{vertical, slug}
|
||||
where := "s.vertical = $1 AND s.slug = $2"
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -933,7 +942,7 @@ func (a *app) listChannels(ctx context.Context, w http.ResponseWriter, r *http.R
|
||||
args = append(args, section)
|
||||
where += fmt.Sprintf(" AND s.slug = $%d", len(args))
|
||||
}
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -990,7 +999,7 @@ func (a *app) createChannel(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
writeError(w, http.StatusBadRequest, "identifier, vertical and section are required")
|
||||
return
|
||||
}
|
||||
section, err := a.findSection(ctx, payload.Vertical, payload.Section, scope)
|
||||
section, err := a.findSection(ctx, payload.Vertical, payload.Section, scope.forManageLookup())
|
||||
if err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
@@ -1083,7 +1092,7 @@ func (a *app) updateChannel(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, err := a.findChannel(ctx, id, scope, r.URL.Query().Get("vertical"), r.URL.Query().Get("section")); err != nil {
|
||||
if _, err := a.findChannel(ctx, id, scope.forManageLookup(), r.URL.Query().Get("vertical"), r.URL.Query().Get("section")); err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
}
|
||||
@@ -1110,7 +1119,7 @@ func (a *app) updateChannel(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
if payload.Vertical != nil && strings.TrimSpace(*payload.Vertical) != "" {
|
||||
vertical = strings.TrimSpace(*payload.Vertical)
|
||||
}
|
||||
section, err := a.findSection(ctx, vertical, strings.TrimSpace(*payload.Section), scope)
|
||||
section, err := a.findSection(ctx, vertical, strings.TrimSpace(*payload.Section), scope.forManageLookup())
|
||||
if err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
@@ -1144,7 +1153,7 @@ func (a *app) deleteChannel(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, err := a.findChannel(ctx, id, scope, r.URL.Query().Get("vertical"), r.URL.Query().Get("section")); err != nil {
|
||||
if _, err := a.findChannel(ctx, id, scope.forManageLookup(), r.URL.Query().Get("vertical"), r.URL.Query().Get("section")); err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
}
|
||||
@@ -1166,7 +1175,7 @@ func (a *app) findChannel(ctx context.Context, id int64, scope accessScope, vert
|
||||
args = append(args, strings.TrimSpace(section))
|
||||
where += fmt.Sprintf(" AND s.slug = $%d", len(args))
|
||||
}
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -1234,7 +1243,7 @@ func (a *app) reanalyzeChannel(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ch, err := a.findChannel(ctx, id, scope, r.URL.Query().Get("vertical"), r.URL.Query().Get("section"))
|
||||
ch, err := a.findChannel(ctx, id, scope.forManageLookup(), r.URL.Query().Get("vertical"), r.URL.Query().Get("section"))
|
||||
if err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
@@ -1316,7 +1325,7 @@ func (a *app) handleMessages(ctx context.Context, w http.ResponseWriter, r *http
|
||||
args = append(args, key, field)
|
||||
where += fmt.Sprintf(" AND COALESCE(mc.verdict ->> $%d, m.extracted -> $%d ->> $%d) = 'true'", len(args), len(args)-1, len(args))
|
||||
}
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -1385,7 +1394,7 @@ func (a *app) handleMessageItem(ctx context.Context, w http.ResponseWriter, r *h
|
||||
}
|
||||
args := []any{id}
|
||||
where := "m.id = $1"
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -1508,7 +1517,7 @@ func (a *app) canReadChannelMedia(ctx context.Context, scope accessScope, channe
|
||||
FROM channels c
|
||||
JOIN sections s ON s.id = c.section_id
|
||||
WHERE c.id = $1 OR c.source_channel_id = $1
|
||||
`, channelID, scope.departmentIDs(), scope.IsAdmin).Scan(&allowed)
|
||||
`, channelID, scope.departmentIDs(), scope.canReadAll()).Scan(&allowed)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return false, nil
|
||||
}
|
||||
@@ -1538,7 +1547,7 @@ func (a *app) handleStats(ctx context.Context, w http.ResponseWriter, r *http.Re
|
||||
args = append(args, section)
|
||||
where += fmt.Sprintf(" AND s.slug = $%d", len(args))
|
||||
}
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -1657,7 +1666,7 @@ func (a *app) pendingLLM(ctx context.Context, scope accessScope, vertical, secti
|
||||
args = append(args, section)
|
||||
where += fmt.Sprintf(" AND s.slug = $%d", len(args))
|
||||
}
|
||||
if !scope.IsAdmin {
|
||||
if !scope.canReadAll() {
|
||||
var deptFilter string
|
||||
args, deptFilter = appendDepartmentFilter(args, scope, "s.department_id")
|
||||
where += deptFilter
|
||||
@@ -1748,7 +1757,7 @@ func (a *app) savePrompt(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||
writeError(w, http.StatusBadRequest, "prompt is too long (max 30000 chars)")
|
||||
return
|
||||
}
|
||||
deptID, err := a.promptDepartmentID(ctx, scope, vertical, section)
|
||||
deptID, err := a.promptDepartmentID(ctx, scope.forManageLookup(), vertical, section)
|
||||
if err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
@@ -1776,7 +1785,7 @@ func (a *app) resetPrompt(ctx context.Context, w http.ResponseWriter, r *http.Re
|
||||
return
|
||||
}
|
||||
section := strings.TrimSpace(r.URL.Query().Get("section"))
|
||||
deptID, err := a.promptDepartmentID(ctx, scope, vertical, section)
|
||||
deptID, err := a.promptDepartmentID(ctx, scope.forManageLookup(), vertical, section)
|
||||
if err != nil {
|
||||
writeDBError(w, err)
|
||||
return
|
||||
@@ -1881,11 +1890,11 @@ func (a *app) readScope(w http.ResponseWriter, r *http.Request, manage bool) (ac
|
||||
writeError(w, http.StatusNotFound, "not found")
|
||||
return scope, false
|
||||
}
|
||||
} else if !scope.IsAdmin && len(scope.departmentIDs()) == 0 {
|
||||
} else if !scope.canReadAll() && len(scope.departmentIDs()) == 0 {
|
||||
writeError(w, http.StatusForbidden, "department is required")
|
||||
return scope, false
|
||||
}
|
||||
if manage && !scope.IsAdmin && len(scope.departmentIDs()) == 0 {
|
||||
if manage && !scope.CanManageAll && len(scope.departmentIDs()) == 0 {
|
||||
writeError(w, http.StatusForbidden, "department is required")
|
||||
return scope, false
|
||||
}
|
||||
@@ -1895,22 +1904,37 @@ func (a *app) readScope(w http.ResponseWriter, r *http.Request, manage bool) (ac
|
||||
func readAccess(r *http.Request) accessScope {
|
||||
admin := commonmw.HeaderBool(r, "X-User-Is-Admin")
|
||||
deptHead := commonmw.HeaderBool(r, "X-User-Is-Department-Head")
|
||||
canManage := commonmw.HeaderBool(r, "X-Monitoring-TG-Can-Manage")
|
||||
canManagePermission := commonmw.HeaderBool(r, "X-Monitoring-TG-Can-Manage")
|
||||
canAuth := commonmw.HeaderBool(r, "X-Monitoring-TG-Can-Auth")
|
||||
canViewAll := commonmw.HeaderBool(r, "X-Monitoring-TG-Can-View-All")
|
||||
deptID := strings.TrimSpace(r.Header.Get("X-User-Department-Id"))
|
||||
deptIDs := commonmw.HeaderCSV(r, "X-User-Department-Ids")
|
||||
if deptID != "" {
|
||||
deptIDs = appendUniqueString(deptIDs, deptID)
|
||||
}
|
||||
return accessScope{
|
||||
IsAdmin: admin,
|
||||
CanManage: admin || deptHead || canManage,
|
||||
CanAuth: admin || canAuth,
|
||||
DeptID: deptID,
|
||||
DeptIDs: deptIDs,
|
||||
IsAdmin: admin,
|
||||
CanManage: admin || deptHead || canManagePermission,
|
||||
CanManageAll: admin || (canManagePermission && canViewAll),
|
||||
CanAuth: admin || canAuth,
|
||||
CanViewAll: admin || canViewAll,
|
||||
DeptID: deptID,
|
||||
DeptIDs: deptIDs,
|
||||
}
|
||||
}
|
||||
|
||||
func (s accessScope) canReadAll() bool {
|
||||
return s.IsAdmin || s.CanViewAll
|
||||
}
|
||||
|
||||
func (s accessScope) forManageLookup() accessScope {
|
||||
if s.CanManageAll {
|
||||
return s
|
||||
}
|
||||
s.CanViewAll = false
|
||||
return s
|
||||
}
|
||||
|
||||
func appendUniqueString(items []string, value string) []string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
|
||||
Reference in New Issue
Block a user