mirror of
https://github.com/knadh/listmonk.git
synced 2025-03-01 08:45:28 +08:00
Update user APIs and queries to embed role + list permissions.
This commit is contained in:
parent
612c1d6eac
commit
d52eac0948
7 changed files with 108 additions and 66 deletions
|
@ -54,10 +54,10 @@ async function initConfig(app) {
|
|||
// one of campaigns:get, campaigns:manage etc. are present.
|
||||
if (perm.endsWith('*')) {
|
||||
const group = `${perm.split(':')[0]}:`;
|
||||
return profile.permissions.some((p) => p.startsWith(group));
|
||||
return profile.role.permissions.some((p) => p.startsWith(group));
|
||||
}
|
||||
|
||||
return profile.permissions.includes(perm);
|
||||
return profile.role.permissions.includes(perm);
|
||||
};
|
||||
|
||||
// Set the page title after i18n has loaded.
|
||||
|
|
|
@ -198,7 +198,7 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
mounted() {
|
||||
this.form = { ...this.form, ...this.$props.data };
|
||||
this.form = { ...this.form, ...this.$props.data, roleId: this.$props.data.role.id };
|
||||
|
||||
this.$api.getRoles();
|
||||
|
||||
|
|
|
@ -52,8 +52,8 @@
|
|||
|
||||
<b-table-column v-slot="props" field="status" :label="$tc('users.role')" header-class="cy-status" sortable
|
||||
:td-attrs="$utils.tdID">
|
||||
<b-tag :class="props.row.roleId === 1 ? 'enabled' : ''">
|
||||
{{ props.row.roleName }}
|
||||
<b-tag :class="props.row.role.id === 1 ? 'enabled' : ''">
|
||||
{{ props.row.role.name }}
|
||||
</b-tag>
|
||||
<b-tag v-if="props.row.type === 'api'" class="primary">
|
||||
<b-icon icon="code" />
|
||||
|
|
|
@ -2,6 +2,7 @@ package core
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/knadh/listmonk/internal/utils"
|
||||
|
@ -13,47 +14,19 @@ import (
|
|||
|
||||
// GetUsers retrieves all users.
|
||||
func (c *Core) GetUsers() ([]models.User, error) {
|
||||
out := []models.User{}
|
||||
if err := c.q.GetUsers.Select(&out, 0); err != nil {
|
||||
return nil, echo.NewHTTPError(http.StatusInternalServerError,
|
||||
c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.users}", "error", pqErrMsg(err)))
|
||||
}
|
||||
|
||||
for n, u := range out {
|
||||
if u.Password.String != "" {
|
||||
u.HasPassword = true
|
||||
u.PasswordLogin = true
|
||||
// u.Password = null.String{}
|
||||
|
||||
out[n] = u
|
||||
}
|
||||
|
||||
if u.Type == models.UserTypeAPI {
|
||||
out[n].Email = null.String{}
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
out, err := c.getUsers(0, "", "")
|
||||
return out, err
|
||||
}
|
||||
|
||||
// GetUser retrieves a specific user based on any one given identifier.
|
||||
func (c *Core) GetUser(id int, username, email string) (models.User, error) {
|
||||
var out models.User
|
||||
if err := c.q.GetUser.Get(&out, id, username, email); err != nil {
|
||||
return out, echo.NewHTTPError(http.StatusInternalServerError,
|
||||
out, err := c.getUsers(id, username, email)
|
||||
if err != nil {
|
||||
return models.User{}, echo.NewHTTPError(http.StatusInternalServerError,
|
||||
c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.users}", "error", pqErrMsg(err)))
|
||||
}
|
||||
if out.Password.String != "" {
|
||||
out.HasPassword = true
|
||||
out.PasswordLogin = true
|
||||
}
|
||||
|
||||
out.PermissionsMap = make(map[string]struct{})
|
||||
for _, p := range out.Permissions {
|
||||
out.PermissionsMap[p] = struct{}{}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
return out[0], nil
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
|
@ -146,9 +119,56 @@ func (c *Core) LoginUser(username, password string) (models.User, error) {
|
|||
c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.users}", "error", pqErrMsg(err)))
|
||||
}
|
||||
|
||||
out.PermissionsMap = make(map[string]struct{})
|
||||
for _, p := range out.Permissions {
|
||||
out.PermissionsMap[p] = struct{}{}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Core) getUsers(id int, username, email string) ([]models.User, error) {
|
||||
out := []models.User{}
|
||||
if err := c.q.GetUsers.Select(&out, id, username, email); err != nil {
|
||||
return nil, echo.NewHTTPError(http.StatusInternalServerError,
|
||||
c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.users}", "error", pqErrMsg(err)))
|
||||
}
|
||||
|
||||
for n, u := range out {
|
||||
if u.Password.String != "" {
|
||||
u.HasPassword = true
|
||||
u.PasswordLogin = true
|
||||
}
|
||||
|
||||
if u.Type == models.UserTypeAPI {
|
||||
u.Email = null.String{}
|
||||
}
|
||||
|
||||
// Unmarshall the raw list perms map.
|
||||
var listPerms []models.ListPermission
|
||||
if u.ListsPermsRaw != nil {
|
||||
if err := json.Unmarshal(u.ListsPermsRaw, &listPerms); err != nil {
|
||||
c.log.Printf("error unmarshalling list permissions for role %d: %v", u.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
u.Role.ID = u.RoleID
|
||||
u.Role.Name = u.RoleName
|
||||
u.Role.Permissions = u.RolePerms
|
||||
u.Role.Lists = listPerms
|
||||
u.RoleID = 0
|
||||
|
||||
// Prepare lookup maps.
|
||||
u.PermissionsMap = make(map[string]struct{})
|
||||
for _, p := range u.RolePerms {
|
||||
u.PermissionsMap[p] = struct{}{}
|
||||
}
|
||||
|
||||
u.ListPermissionsMap = make(map[int]map[string]struct{})
|
||||
for _, p := range listPerms {
|
||||
u.ListPermissionsMap[p.ID] = make(map[string]struct{})
|
||||
|
||||
for _, perm := range p.Permissions {
|
||||
u.ListPermissionsMap[p.ID][perm] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
out[n] = u
|
||||
}
|
||||
|
||||
return out, nil
|
||||
|
|
|
@ -156,15 +156,25 @@ type User struct {
|
|||
Email null.String `db:"email" json:"email"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Type string `db:"type" json:"type"`
|
||||
RoleID int `db:"role_id" json:"role_id"`
|
||||
RoleName string `db:"role_name" json:"role_name"`
|
||||
Permissions pq.StringArray `db:"permissions" json:"permissions"`
|
||||
Status string `db:"status" json:"status"`
|
||||
Avatar null.String `db:"-" json:"avatar"`
|
||||
|
||||
PermissionsMap map[string]struct{} `db:"-" json:"-"`
|
||||
LoggedInAt null.Time `db:"loggedin_at" json:"loggedin_at"`
|
||||
|
||||
// Filled post-retrieval.
|
||||
Role struct {
|
||||
ID int `db:"-" json:"id"`
|
||||
Name string `db:"-" json:"name"`
|
||||
Permissions []string `db:"-" json:"permissions"`
|
||||
Lists []ListPermission `db:"-" json:"lists"`
|
||||
} `db:"-" json:"role"`
|
||||
|
||||
RoleID int `db:"role_id" json:"role_id,omitempty"`
|
||||
RoleName string `db:"role_name" json:"-"`
|
||||
RolePerms pq.StringArray `db:"role_permissions" json:"-"`
|
||||
ListsPermsRaw json.RawMessage `db:"list_permissions" json:"-"`
|
||||
|
||||
PermissionsMap map[string]struct{} `db:"-" json:"-"`
|
||||
ListPermissionsMap map[int]map[string]struct{} `db:"-" json:"-"`
|
||||
HasPassword bool `db:"-" json:"-"`
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,6 @@ type Queries struct {
|
|||
UpdateUserProfile *sqlx.Stmt `query:"update-user-profile"`
|
||||
DeleteUsers *sqlx.Stmt `query:"delete-users"`
|
||||
GetUsers *sqlx.Stmt `query:"get-users"`
|
||||
GetUser *sqlx.Stmt `query:"get-user"`
|
||||
GetAPITokens *sqlx.Stmt `query:"get-api-tokens"`
|
||||
LoginUser *sqlx.Stmt `query:"login-user"`
|
||||
|
||||
|
|
33
queries.sql
33
queries.sql
|
@ -1062,22 +1062,35 @@ WITH u AS (
|
|||
)
|
||||
DELETE FROM users WHERE id = ALL($1) AND (SELECT num FROM u) > 0;
|
||||
|
||||
-- name: get-user
|
||||
SELECT users.*, r.name as role_name, r.permissions FROM users
|
||||
LEFT JOIN user_roles r ON (r.id = users.role_id)
|
||||
-- name: get-users
|
||||
WITH u AS (
|
||||
SELECT * FROM users
|
||||
WHERE
|
||||
(
|
||||
CASE
|
||||
WHEN $1::INT != 0 THEN users.id = $1
|
||||
WHEN $2::TEXT != '' THEN username = $2
|
||||
WHEN $3::TEXT != '' THEN email = $3
|
||||
ELSE TRUE
|
||||
END
|
||||
);
|
||||
)
|
||||
),
|
||||
role AS (
|
||||
SELECT id, name, permissions FROM user_roles WHERE id IN (SELECT role_id FROM users)
|
||||
),
|
||||
listPerms AS (
|
||||
SELECT ur.parent_id, JSONB_AGG(JSONB_BUILD_OBJECT('id', ur.list_id, 'name', lists.name, 'permissions', ur.permissions)) AS listPerms
|
||||
FROM user_roles ur
|
||||
LEFT JOIN lists ON(lists.id = ur.list_id)
|
||||
WHERE ur.parent_id IS NOT NULL GROUP BY ur.parent_id
|
||||
),
|
||||
roleInfo AS (
|
||||
SELECT role.id AS role_id, role.name AS role_name, role.permissions AS role_permissions, COALESCE(l.listPerms, '[]'::JSONB) AS "list_permissions"
|
||||
FROM role
|
||||
LEFT JOIN listPerms l ON role.id = l.parent_id
|
||||
)
|
||||
SELECT u.*, ri.* FROM u JOIN roleInfo ri ON u.role_id = ri.role_id;
|
||||
|
||||
-- name: get-users
|
||||
SELECT users.*, r.name as role_name, r.permissions FROM users
|
||||
LEFT JOIN user_roles r ON (r.id = users.role_id)
|
||||
WHERE $1=0 OR users.id=$1 ORDER BY created_at;
|
||||
|
||||
-- name: get-api-tokens
|
||||
SELECT username, password FROM users WHERE status='enabled' AND type='api';
|
||||
|
@ -1099,14 +1112,14 @@ UPDATE users SET name=$2, email=$3,
|
|||
WITH mainroles AS (
|
||||
SELECT ur.* FROM user_roles ur WHERE ur.parent_id IS NULL
|
||||
),
|
||||
listroles AS (
|
||||
listPerms AS (
|
||||
SELECT ur.parent_id, JSONB_AGG(JSONB_BUILD_OBJECT('id', ur.list_id, 'name', lists.name, 'permissions', ur.permissions)) AS listPerms
|
||||
FROM user_roles ur
|
||||
LEFT JOIN lists ON(lists.id = ur.list_id)
|
||||
WHERE ur.parent_id IS NOT NULL GROUP BY ur.parent_id
|
||||
)
|
||||
SELECT p.*, COALESCE(l.listPerms, '[]'::JSONB) AS "list_permissions" FROM mainroles p
|
||||
LEFT JOIN listroles l ON p.id = l.parent_id;
|
||||
LEFT JOIN listPerms l ON p.id = l.parent_id;
|
||||
|
||||
-- name: create-role
|
||||
INSERT INTO user_roles (name, permissions, created_at, updated_at) VALUES($1, $2, NOW(), NOW()) RETURNING *;
|
||||
|
|
Loading…
Reference in a new issue