Skip to content

Commit 8eacf6d

Browse files
committed
feat: Adds grants to the roles
This will allow us to display all the different grants on the dashboard
1 parent 8baf701 commit 8eacf6d

File tree

10 files changed

+229
-74
lines changed

10 files changed

+229
-74
lines changed

dist/api/roles.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
"use strict";
2+
var __assign = (this && this.__assign) || function () {
3+
__assign = Object.assign || function(t) {
4+
for (var s, i = 1, n = arguments.length; i < n; i++) {
5+
s = arguments[i];
6+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7+
t[p] = s[p];
8+
}
9+
return t;
10+
};
11+
return __assign.apply(this, arguments);
12+
};
113
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
214
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
315
return new (P || (P = Promise))(function (resolve, reject) {
@@ -34,21 +46,26 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
3446
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
3547
}
3648
};
37-
var _this = this;
49+
Object.defineProperty(exports, "__esModule", { value: true });
3850
var Router = require('express').Router;
3951
var router = new Router();
4052
var roles = require('../lib/sql').roles;
4153
var RunQuery = require('../lib/connectionPool');
42-
router.get('/', function (req, res) { return __awaiter(_this, void 0, void 0, function () {
43-
var data, error_1;
54+
var schemas_1 = require("../lib/constants/schemas");
55+
router.get('/', function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
56+
var data, query, payload, error_1;
4457
return __generator(this, function (_a) {
4558
switch (_a.label) {
4659
case 0:
4760
_a.trys.push([0, 2, , 3]);
4861
return [4 /*yield*/, RunQuery(req.headers.pg, roles.list)];
4962
case 1:
5063
data = (_a.sent()).data;
51-
return [2 /*return*/, res.status(200).json(data)];
64+
query = req.query;
65+
payload = data;
66+
if (!(query === null || query === void 0 ? void 0 : query.includeSystemSchemas))
67+
payload = removeSystemSchemas(data);
68+
return [2 /*return*/, res.status(200).json(payload)];
5269
case 2:
5370
error_1 = _a.sent();
5471
console.log('throwing error');
@@ -58,4 +75,10 @@ router.get('/', function (req, res) { return __awaiter(_this, void 0, void 0, fu
5875
}
5976
});
6077
}); });
78+
var removeSystemSchemas = function (data) {
79+
return data.map(function (role) {
80+
var grants = role.grants.filter(function (x) { return !schemas_1.DEFAULT_SYSTEM_SCHEMAS.includes(x.schema); });
81+
return __assign(__assign({}, role), { grants: grants });
82+
});
83+
};
6184
module.exports = router;

dist/lib/interfaces/roles.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });

dist/lib/sql/roles/list.sql

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,61 @@
1-
select
2-
usename as "name",
3-
usesysid as "id",
4-
usecreatedb as "has_create_db_privileges",
5-
usesuper as "is_super_user",
6-
userepl as "has_replication_privileges",
7-
usebypassrls as "can_bypass_rls",
8-
valuntil as "valid_until", -- Password expiry time (only used for password authentication)
9-
useconfig as "user_config", -- Session defaults for run-time configuration variables
10-
active_connections.connections,
11-
pg_roles.rolconnlimit as max_user_connections,
12-
max_db_connections.max_connections as max_db_connections
13-
from
14-
pg_user as users
15-
inner join pg_roles on users.usename = pg_roles.rolname
16-
INNER JOIN LATERAL
17-
(
18-
SELECT count(*) as connections
19-
FROM pg_stat_activity as active_connections
20-
WHERE state = 'active' and users.usename = active_connections.usename
21-
) as active_connections on 1=1
22-
INNER JOIN LATERAL (
23-
SELECT setting as max_connections
24-
from pg_settings where name = 'max_connections'
25-
) as max_db_connections on 1=1;
1+
with
2+
roles as (
3+
select
4+
usename as "name",
5+
usesysid as "id",
6+
usecreatedb as "has_create_db_privileges",
7+
usesuper as "is_super_user",
8+
userepl as "has_replication_privileges",
9+
usebypassrls as "can_bypass_rls",
10+
valuntil as "valid_until", -- Password expiry time (only used for password authentication)
11+
useconfig as "user_config", -- Session defaults for run-time configuration variables
12+
active_connections.connections,
13+
pg_roles.rolconnlimit as max_user_connections,
14+
max_db_connections.max_connections::int2 as max_db_connections
15+
from
16+
pg_user as users
17+
inner join pg_roles on users.usename = pg_roles.rolname
18+
INNER JOIN LATERAL
19+
(
20+
SELECT count(*) as connections
21+
FROM pg_stat_activity as active_connections
22+
WHERE state = 'active' and users.usename = active_connections.usename
23+
) as active_connections on 1=1
24+
INNER JOIN LATERAL (
25+
SELECT setting as max_connections from pg_settings where name = 'max_connections'
26+
) as max_db_connections on 1=1
27+
),
28+
grants as (
29+
SELECT
30+
(table_schema || '.' || table_name) as table_id,
31+
grantor,
32+
grantee,
33+
table_catalog as catalog,
34+
table_schema as schema,
35+
table_name,
36+
privilege_type,
37+
is_grantable::boolean,
38+
with_hierarchy::boolean
39+
FROM
40+
information_schema.role_table_grants
41+
)
42+
SELECT
43+
*,
44+
COALESCE(
45+
(
46+
SELECT
47+
array_to_json(array_agg(row_to_json(grants)))
48+
FROM
49+
(
50+
SELECT
51+
*
52+
FROM
53+
grants
54+
WHERE
55+
grants.grantee = roles.name
56+
) grants
57+
),
58+
'[]'
59+
) AS grants
60+
FROM
61+
roles

dist/lib/sql/tables/list.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ grants as (
7070
table_schema as schema,
7171
table_name,
7272
privilege_type,
73-
is_grantable,
74-
with_hierarchy
73+
is_grantable::boolean,
74+
with_hierarchy::boolean
7575
FROM
7676
information_schema.role_table_grants
7777
),

src/api/roles.js

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/api/roles.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const { Router } = require('express')
2+
const router = new Router()
3+
const { roles } = require('../lib/sql')
4+
const RunQuery = require('../lib/connectionPool')
5+
import { DEFAULT_SYSTEM_SCHEMAS } from '../lib/constants/schemas'
6+
import { Roles } from '../lib/interfaces/roles'
7+
8+
/**
9+
* @param {boolean} [includeSystemSchemas=false] - Return system schemas as well as user schemas
10+
*/
11+
interface GetRolesQueryParams {
12+
includeSystemSchemas: boolean
13+
}
14+
router.get('/', async (req, res) => {
15+
try {
16+
const { data } = await RunQuery(req.headers.pg, roles.list)
17+
const query: GetRolesQueryParams = req.query
18+
let payload: Roles.Role[] = data
19+
if (!query?.includeSystemSchemas) payload = removeSystemSchemas(data)
20+
21+
return res.status(200).json(payload)
22+
} catch (error) {
23+
console.log('throwing error')
24+
res.status(500).json({ error: 'Database error', status: 500 })
25+
}
26+
})
27+
28+
29+
const removeSystemSchemas = (data: Roles.Role[]) => {
30+
return data.map(role => {
31+
let grants = role.grants.filter((x) => !DEFAULT_SYSTEM_SCHEMAS.includes(x.schema))
32+
return {
33+
...role,
34+
grants
35+
}
36+
})
37+
}
38+
39+
module.exports = router

src/lib/interfaces/roles.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export namespace Roles {
2+
3+
export interface Role {
4+
name: string
5+
id: number
6+
has_create_db_privileges: boolean
7+
is_super_user: boolean
8+
has_replication_privileges: boolean
9+
can_bypass_rls: boolean
10+
valid_until: string | null
11+
user_config: string | null
12+
connections: number
13+
max_user_connections: number
14+
max_db_connections: number,
15+
grants: Grants[]
16+
}
17+
18+
export interface Grants {
19+
table_id: string
20+
grantor: string
21+
grantee: string
22+
catalog: string
23+
schema: string
24+
table_name: string
25+
privilege_type: string
26+
is_grantable: boolean
27+
with_hierarchy: boolean
28+
}
29+
30+
31+
}

src/lib/sql/roles/list.sql

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,61 @@
1-
select
2-
usename as "name",
3-
usesysid as "id",
4-
usecreatedb as "has_create_db_privileges",
5-
usesuper as "is_super_user",
6-
userepl as "has_replication_privileges",
7-
usebypassrls as "can_bypass_rls",
8-
valuntil as "valid_until", -- Password expiry time (only used for password authentication)
9-
useconfig as "user_config", -- Session defaults for run-time configuration variables
10-
active_connections.connections,
11-
pg_roles.rolconnlimit as max_user_connections,
12-
max_db_connections.max_connections as max_db_connections
13-
from
14-
pg_user as users
15-
inner join pg_roles on users.usename = pg_roles.rolname
16-
INNER JOIN LATERAL
17-
(
18-
SELECT count(*) as connections
19-
FROM pg_stat_activity as active_connections
20-
WHERE state = 'active' and users.usename = active_connections.usename
21-
) as active_connections on 1=1
22-
INNER JOIN LATERAL (
23-
SELECT setting as max_connections
24-
from pg_settings where name = 'max_connections'
25-
) as max_db_connections on 1=1;
1+
with
2+
roles as (
3+
select
4+
usename as "name",
5+
usesysid as "id",
6+
usecreatedb as "has_create_db_privileges",
7+
usesuper as "is_super_user",
8+
userepl as "has_replication_privileges",
9+
usebypassrls as "can_bypass_rls",
10+
valuntil as "valid_until", -- Password expiry time (only used for password authentication)
11+
useconfig as "user_config", -- Session defaults for run-time configuration variables
12+
active_connections.connections,
13+
pg_roles.rolconnlimit as max_user_connections,
14+
max_db_connections.max_connections::int2 as max_db_connections
15+
from
16+
pg_user as users
17+
inner join pg_roles on users.usename = pg_roles.rolname
18+
INNER JOIN LATERAL
19+
(
20+
SELECT count(*) as connections
21+
FROM pg_stat_activity as active_connections
22+
WHERE state = 'active' and users.usename = active_connections.usename
23+
) as active_connections on 1=1
24+
INNER JOIN LATERAL (
25+
SELECT setting as max_connections from pg_settings where name = 'max_connections'
26+
) as max_db_connections on 1=1
27+
),
28+
grants as (
29+
SELECT
30+
(table_schema || '.' || table_name) as table_id,
31+
grantor,
32+
grantee,
33+
table_catalog as catalog,
34+
table_schema as schema,
35+
table_name,
36+
privilege_type,
37+
is_grantable::boolean,
38+
with_hierarchy::boolean
39+
FROM
40+
information_schema.role_table_grants
41+
)
42+
SELECT
43+
*,
44+
COALESCE(
45+
(
46+
SELECT
47+
array_to_json(array_agg(row_to_json(grants)))
48+
FROM
49+
(
50+
SELECT
51+
*
52+
FROM
53+
grants
54+
WHERE
55+
grants.grantee = roles.name
56+
) grants
57+
),
58+
'[]'
59+
) AS grants
60+
FROM
61+
roles

src/lib/sql/tables/list.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ grants as (
7070
table_schema as schema,
7171
table_name,
7272
privilege_type,
73-
is_grantable,
74-
with_hierarchy
73+
is_grantable::boolean,
74+
with_hierarchy::boolean
7575
FROM
7676
information_schema.role_table_grants
7777
),

test/integration/index.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,11 @@ describe('/roles', () => {
176176
it('GET', async () => {
177177
const res = await axios.get(`${URL}/roles`)
178178
const datum = res.data.find((x) => x.name == 'postgres')
179+
const hasSystemSchema = res.data[0].grants.some((x) => x.schema == 'information_schema')
180+
const hasPublicSchema = res.data[0].grants.some((x) => x.schema == 'public')
179181
assert.equal(res.status, STATUS.SUCCESS)
180182
assert.equal(true, !!datum)
183+
assert.equal(hasSystemSchema, false)
184+
assert.equal(hasPublicSchema, true)
181185
})
182186
})

0 commit comments

Comments
 (0)