@pgpm/encrypted-secrets 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +22 -0
  2. package/Makefile +6 -0
  3. package/README.md +5 -0
  4. package/__tests__/__snapshots__/secrets.test.ts.snap +28 -0
  5. package/__tests__/secrets.test.ts +193 -0
  6. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text.sql +17 -0
  7. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_crypt.sql +17 -0
  8. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify.sql +37 -0
  9. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_pgp.sql +17 -0
  10. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_pgp_get.sql +18 -0
  11. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter.sql +42 -0
  12. package/deploy/schemas/encrypted_secrets/procedures/encrypt_field_set.sql +16 -0
  13. package/deploy/schemas/encrypted_secrets/procedures/secrets_delete.sql +40 -0
  14. package/deploy/schemas/encrypted_secrets/procedures/secrets_getter.sql +43 -0
  15. package/deploy/schemas/encrypted_secrets/procedures/secrets_table_upsert.sql +67 -0
  16. package/deploy/schemas/encrypted_secrets/procedures/secrets_upsert.sql +33 -0
  17. package/deploy/schemas/encrypted_secrets/procedures/secrets_verify.sql +46 -0
  18. package/deploy/schemas/encrypted_secrets/schema.sql +8 -0
  19. package/jest.config.js +15 -0
  20. package/launchql-encrypted-secrets.control +8 -0
  21. package/launchql.plan +17 -0
  22. package/package.json +30 -0
  23. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text.sql +7 -0
  24. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_crypt.sql +7 -0
  25. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify.sql +7 -0
  26. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_pgp.sql +7 -0
  27. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_pgp_get.sql +7 -0
  28. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter.sql +7 -0
  29. package/revert/schemas/encrypted_secrets/procedures/encrypt_field_set.sql +7 -0
  30. package/revert/schemas/encrypted_secrets/procedures/secrets_delete.sql +8 -0
  31. package/revert/schemas/encrypted_secrets/procedures/secrets_getter.sql +7 -0
  32. package/revert/schemas/encrypted_secrets/procedures/secrets_table_upsert.sql +7 -0
  33. package/revert/schemas/encrypted_secrets/procedures/secrets_upsert.sql +7 -0
  34. package/revert/schemas/encrypted_secrets/procedures/secrets_verify.sql +7 -0
  35. package/revert/schemas/encrypted_secrets/schema.sql +7 -0
  36. package/sql/launchql-encrypted-secrets--0.1.0.sql +231 -0
  37. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text.sql +7 -0
  38. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_crypt.sql +7 -0
  39. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify.sql +7 -0
  40. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_pgp.sql +7 -0
  41. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_pgp_get.sql +7 -0
  42. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter.sql +7 -0
  43. package/verify/schemas/encrypted_secrets/procedures/encrypt_field_set.sql +7 -0
  44. package/verify/schemas/encrypted_secrets/procedures/secrets_delete.sql +7 -0
  45. package/verify/schemas/encrypted_secrets/procedures/secrets_getter.sql +7 -0
  46. package/verify/schemas/encrypted_secrets/procedures/secrets_table_upsert.sql +7 -0
  47. package/verify/schemas/encrypted_secrets/procedures/secrets_upsert.sql +7 -0
  48. package/verify/schemas/encrypted_secrets/procedures/secrets_verify.sql +7 -0
  49. package/verify/schemas/encrypted_secrets/schema.sql +7 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Dan Lynch <pyramation@gmail.com>
4
+ Copyright (c) 2025 Interweb, Inc.
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/Makefile ADDED
@@ -0,0 +1,6 @@
1
+ EXTENSION = launchql-encrypted-secrets
2
+ DATA = sql/launchql-encrypted-secrets--0.1.0.sql
3
+
4
+ PG_CONFIG = pg_config
5
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
6
+ include $(PGXS)
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @pgpm/encrypted-secrets
2
+
3
+ Encrypted secrets management for PostgreSQL.
4
+
5
+ Provides secure storage and retrieval of encrypted secrets, API keys, and sensitive configuration data within PostgreSQL.
@@ -0,0 +1,28 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`encrypted secrets encrypt_field_bytea_to_text 1`] = `"value-there-and-back"`;
4
+
5
+ exports[`encrypted secrets encrypt_field_pgp_get 1`] = `"my-secret"`;
6
+
7
+ exports[`encrypted secrets encrypt_field_set 1`] = `
8
+ {
9
+ "data": [
10
+ 109,
11
+ 121,
12
+ 118,
13
+ 97,
14
+ 108,
15
+ 117,
16
+ 101,
17
+ ],
18
+ "type": "Buffer",
19
+ }
20
+ `;
21
+
22
+ exports[`encrypted secrets secrets_getter 1`] = `"my-secret"`;
23
+
24
+ exports[`encrypted secrets secrets_upsert 1`] = `true`;
25
+
26
+ exports[`encrypted secrets secrets_upsert 2`] = `true`;
27
+
28
+ exports[`encrypted secrets secrets_verify 1`] = `true`;
@@ -0,0 +1,193 @@
1
+ import { getConnections, PgTestClient } from 'pgsql-test';
2
+
3
+ let pg: PgTestClient;
4
+ let teardown: () => Promise<void>;
5
+
6
+ const user_id = 'dc474833-318a-41f5-9239-ee563ab657a6';
7
+
8
+ describe('encrypted secrets', () => {
9
+ beforeAll(async () => {
10
+ ({ pg, teardown } = await getConnections());
11
+ });
12
+
13
+ afterAll(async () => {
14
+ await teardown();
15
+ });
16
+
17
+ beforeEach(async () => {
18
+ await pg.beforeEach();
19
+
20
+ // Insert test data
21
+ await pg.any(`
22
+ INSERT INTO secrets_schema.secrets_table
23
+ ( secrets_owned_field,
24
+ name,
25
+ secrets_value_field,
26
+ secrets_enc_field
27
+ ) VALUES
28
+ (
29
+ $1::uuid,
30
+ 'my-secret-name',
31
+ 'my-secret'::bytea,
32
+ 'pgp'
33
+ )
34
+ `, [user_id]);
35
+ });
36
+
37
+ afterEach(async () => {
38
+ await pg.afterEach();
39
+ });
40
+
41
+ it('encrypt_field_pgp_get', async () => {
42
+ const [{ encrypt_field_pgp_get }] = await pg.any(
43
+ `SELECT encrypted_secrets.encrypt_field_pgp_get(secrets_value_field, secrets_owned_field::text)
44
+ FROM secrets_schema.secrets_table
45
+ WHERE secrets_owned_field = $1`,
46
+ [user_id]
47
+ );
48
+ expect(encrypt_field_pgp_get).toMatchSnapshot();
49
+ });
50
+
51
+ it('encrypt_field_set', async () => {
52
+ const [{ encrypt_field_set }] = await pg.any(
53
+ `SELECT encrypted_secrets.encrypt_field_set('myvalue')`
54
+ );
55
+ expect(encrypt_field_set).toMatchSnapshot();
56
+ });
57
+
58
+ it('encrypt_field_bytea_to_text', async () => {
59
+ const [{ encrypt_field_bytea_to_text }] = await pg.any(
60
+ `SELECT encrypted_secrets.encrypt_field_bytea_to_text(
61
+ encrypted_secrets.encrypt_field_set('value-there-and-back')
62
+ )`
63
+ );
64
+ expect(encrypt_field_bytea_to_text).toMatchSnapshot();
65
+ });
66
+
67
+ it('secrets_getter', async () => {
68
+ const [{ secrets_getter }] = await pg.any(
69
+ `SELECT encrypted_secrets.secrets_getter(
70
+ $1::uuid,
71
+ 'my-secret-name'
72
+ )`,
73
+ [user_id]
74
+ );
75
+ expect(secrets_getter).toMatchSnapshot();
76
+ });
77
+
78
+ it('secrets_verify', async () => {
79
+ const [{ secrets_verify }] = await pg.any(
80
+ `SELECT encrypted_secrets.secrets_verify(
81
+ $1::uuid,
82
+ 'my-secret-name',
83
+ 'my-secret'
84
+ )`,
85
+ [user_id]
86
+ );
87
+ expect(secrets_verify).toMatchSnapshot();
88
+ });
89
+
90
+ it('secrets_upsert', async () => {
91
+ const [{ secrets_upsert }] = await pg.any(
92
+ `SELECT encrypted_secrets.secrets_upsert(
93
+ $1::uuid,
94
+ 'my-secret-name',
95
+ 'my-secret-other-value'
96
+ )`,
97
+ [user_id]
98
+ );
99
+ expect(secrets_upsert).toMatchSnapshot();
100
+
101
+ const [{ secrets_verify }] = await pg.any(
102
+ `SELECT encrypted_secrets.secrets_verify(
103
+ $1::uuid,
104
+ 'my-secret-name',
105
+ 'my-secret-other-value'
106
+ )`,
107
+ [user_id]
108
+ );
109
+ expect(secrets_verify).toMatchSnapshot();
110
+ });
111
+
112
+ it('secrets_delete single', async () => {
113
+ // First verify the secret exists
114
+ const [beforeDelete] = await pg.any(
115
+ `SELECT encrypted_secrets.secrets_getter($1::uuid, 'my-secret-name')`,
116
+ [user_id]
117
+ );
118
+ expect(beforeDelete.secrets_getter).toBe('my-secret');
119
+
120
+ // Delete the secret
121
+ await pg.any(
122
+ `SELECT encrypted_secrets.secrets_delete($1::uuid, 'my-secret-name')`,
123
+ [user_id]
124
+ );
125
+
126
+ // Verify it's gone
127
+ const [afterDelete] = await pg.any(
128
+ `SELECT encrypted_secrets.secrets_getter($1::uuid, 'my-secret-name', 'default-value')`,
129
+ [user_id]
130
+ );
131
+ expect(afterDelete.secrets_getter).toBe('default-value');
132
+ });
133
+
134
+ it('secrets_delete multiple', async () => {
135
+ // Add multiple secrets
136
+ await pg.any(
137
+ `SELECT encrypted_secrets.secrets_upsert($1::uuid, 'secret-1', 'value-1')`,
138
+ [user_id]
139
+ );
140
+ await pg.any(
141
+ `SELECT encrypted_secrets.secrets_upsert($1::uuid, 'secret-2', 'value-2')`,
142
+ [user_id]
143
+ );
144
+
145
+ // Delete multiple secrets
146
+ await pg.any(
147
+ `SELECT encrypted_secrets.secrets_delete($1::uuid, $2::text[])`,
148
+ [user_id, ['secret-1', 'secret-2']]
149
+ );
150
+
151
+ // Verify they're gone
152
+ const [result1] = await pg.any(
153
+ `SELECT encrypted_secrets.secrets_getter($1::uuid, 'secret-1', 'not-found')`,
154
+ [user_id]
155
+ );
156
+ const [result2] = await pg.any(
157
+ `SELECT encrypted_secrets.secrets_getter($1::uuid, 'secret-2', 'not-found')`,
158
+ [user_id]
159
+ );
160
+
161
+ expect(result1.secrets_getter).toBe('not-found');
162
+ expect(result2.secrets_getter).toBe('not-found');
163
+ });
164
+
165
+ xit('encrypt_field_pgp_getter', async () => {
166
+ const [{ encrypt_field_pgp_getter }] = await pg.any(
167
+ `SELECT encrypted_secrets.encrypt_field_pgp_getter(
168
+ $1::uuid,
169
+ 'secrets_value_field',
170
+ 'secrets_enc_field'
171
+ )`,
172
+ [user_id]
173
+ );
174
+ expect(encrypt_field_pgp_getter).toMatchSnapshot();
175
+ });
176
+
177
+ xit('secrets_table_upsert', async () => {
178
+ const [{ secrets_table_upsert }] = await pg.any(
179
+ `SELECT encrypted_secrets.secrets_table_upsert(
180
+ $1::uuid,
181
+ $2::json
182
+ )`,
183
+ [
184
+ user_id,
185
+ JSON.stringify({
186
+ myOther: 'secret',
187
+ hiOther: 'here'
188
+ })
189
+ ]
190
+ );
191
+ expect(secrets_table_upsert).toMatchSnapshot();
192
+ });
193
+ });
@@ -0,0 +1,17 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_bytea_to_text(
8
+ secret_value bytea
9
+ )
10
+ RETURNS text
11
+ AS $$
12
+ SELECT
13
+ convert_from(encrypt_field_bytea_to_text.secret_value, 'SQL_ASCII');
14
+ $$
15
+ LANGUAGE 'sql' IMMUTABLE;
16
+
17
+ COMMIT;
@@ -0,0 +1,17 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_crypt to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_crypt()
8
+ RETURNS TRIGGER
9
+ AS $CODEZ$
10
+ BEGIN
11
+ NEW.field_name = crypt(NEW.field_name::text, gen_salt('bf'));
12
+ RETURN NEW;
13
+ END;
14
+ $CODEZ$
15
+ LANGUAGE plpgsql VOLATILE;
16
+
17
+ COMMIT;
@@ -0,0 +1,37 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_crypt_verify(
8
+ secrets_owned_field uuid,
9
+ secret_value_field text,
10
+ secret_verify_value text
11
+ )
12
+ RETURNS bool
13
+ AS $$
14
+ DECLARE
15
+ result bool;
16
+ rec secrets_schema.secrets_table;
17
+ s_value text;
18
+ BEGIN
19
+
20
+ SELECT * FROM secrets_schema.secrets_table s
21
+ WHERE s.secrets_owned_field = encrypt_field_crypt_verify.secrets_owned_field
22
+ INTO rec;
23
+
24
+ EXECUTE format('SELECT ($1).%s::text', secret_value_field)
25
+ USING rec
26
+ INTO s_value;
27
+
28
+ SELECT
29
+ s_value = crypt(secret_verify_value, s_value)
30
+ INTO result;
31
+
32
+ RETURN result;
33
+ END;
34
+ $$
35
+ LANGUAGE 'plpgsql' STABLE;
36
+
37
+ COMMIT;
@@ -0,0 +1,17 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_pgp to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_pgp()
8
+ RETURNS TRIGGER
9
+ AS $CODEZ$
10
+ BEGIN
11
+ NEW.field_name = pgp_sym_encrypt(encode(NEW.field_name::bytea, 'hex'), NEW.encode_field::text, 'compress-algo=1, cipher-algo=aes256');
12
+ RETURN NEW;
13
+ END;
14
+ $CODEZ$
15
+ LANGUAGE plpgsql VOLATILE;
16
+
17
+ COMMIT;
@@ -0,0 +1,18 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_pgp_get to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_pgp_get(
8
+ secret_value bytea,
9
+ secret_encode text
10
+ )
11
+ RETURNS text
12
+ AS $$
13
+ SELECT
14
+ convert_from(decode(pgp_sym_decrypt(encrypt_field_pgp_get.secret_value, encrypt_field_pgp_get.secret_encode), 'hex'), 'SQL_ASCII');
15
+ $$
16
+ LANGUAGE 'sql';
17
+
18
+ COMMIT;
@@ -0,0 +1,42 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_pgp_getter(
8
+ secrets_owned_field uuid,
9
+ secret_value_field text,
10
+ secret_encode_field text
11
+ )
12
+ RETURNS text
13
+ AS $$
14
+ DECLARE
15
+ result text;
16
+ rec secrets_schema.secrets_table;
17
+ s_value bytea;
18
+ s_enc text;
19
+ BEGIN
20
+
21
+ SELECT * FROM secrets_schema.secrets_table s
22
+ WHERE s.secrets_owned_field = encrypt_field_pgp_getter.secrets_owned_field
23
+ INTO rec;
24
+
25
+ EXECUTE format('SELECT ($1).%s::text', secret_value_field)
26
+ USING rec
27
+ INTO s_value;
28
+
29
+ EXECUTE format('SELECT ($1).%s::text', secret_encode_field)
30
+ USING rec
31
+ INTO s_enc;
32
+
33
+ SELECT
34
+ convert_from(decode(pgp_sym_decrypt(s_value, s_enc), 'hex'), 'SQL_ASCII')
35
+ INTO result;
36
+
37
+ RETURN result;
38
+ END;
39
+ $$
40
+ LANGUAGE 'plpgsql' STABLE;
41
+
42
+ COMMIT;
@@ -0,0 +1,16 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/encrypt_field_set to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.encrypt_field_set(
8
+ secret_value text
9
+ )
10
+ RETURNS bytea
11
+ AS $$
12
+ SELECT encrypt_field_set.secret_value::bytea;
13
+ $$
14
+ LANGUAGE 'sql';
15
+
16
+ COMMIT;
@@ -0,0 +1,40 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/secrets_delete to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.secrets_delete(
8
+ secrets_owned_field uuid,
9
+ secret_name text
10
+ )
11
+ RETURNS void
12
+ AS $$
13
+ BEGIN
14
+ DELETE FROM secrets_schema.secrets_table s
15
+ WHERE s.secrets_owned_field = secrets_delete.secrets_owned_field
16
+ AND s.name = secrets_delete.secret_name;
17
+ END
18
+ $$
19
+ LANGUAGE 'plpgsql'
20
+ VOLATILE;
21
+
22
+ CREATE FUNCTION encrypted_secrets.secrets_delete(
23
+ secrets_owned_field uuid,
24
+ secret_names text[]
25
+ )
26
+ RETURNS void
27
+ AS $$
28
+ BEGIN
29
+ DELETE FROM secrets_schema.secrets_table s
30
+ WHERE s.secrets_owned_field = secrets_delete.secrets_owned_field
31
+ AND s.name = ANY(secrets_delete.secret_names);
32
+ END
33
+ $$
34
+ LANGUAGE 'plpgsql'
35
+ VOLATILE;
36
+
37
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_delete(uuid,text) TO authenticated;
38
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_delete(uuid,text[]) TO authenticated;
39
+
40
+ COMMIT;
@@ -0,0 +1,43 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/secrets_getter to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.secrets_getter(
8
+ secrets_owned_field uuid,
9
+ secret_name text,
10
+ default_value text default null
11
+ )
12
+ RETURNS text
13
+ AS $$
14
+ DECLARE
15
+ v_secret secrets_schema.secrets_table;
16
+ BEGIN
17
+ SELECT
18
+ *
19
+ FROM
20
+ secrets_schema.secrets_table s
21
+ WHERE
22
+ s.name = secrets_getter.secret_name
23
+ AND s.secrets_owned_field = secrets_getter.secrets_owned_field
24
+ INTO v_secret;
25
+
26
+ IF (NOT FOUND OR v_secret IS NULL) THEN
27
+ RETURN secrets_getter.default_value;
28
+ END IF;
29
+
30
+ IF (v_secret.secrets_enc_field = 'crypt') THEN
31
+ RETURN convert_from(v_secret.secrets_value_field, 'SQL_ASCII');
32
+ ELSIF (v_secret.secrets_enc_field = 'pgp') THEN
33
+ RETURN convert_from(decode(pgp_sym_decrypt(v_secret.secrets_value_field, v_secret.secrets_owned_field::text), 'hex'), 'SQL_ASCII');
34
+ END IF;
35
+
36
+ RETURN convert_from(v_secret.secrets_value_field, 'SQL_ASCII');
37
+
38
+ END
39
+ $$
40
+ LANGUAGE 'plpgsql'
41
+ STABLE;
42
+
43
+ COMMIT;
@@ -0,0 +1,67 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/secrets_table_upsert to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.secrets_table_upsert(
8
+ secrets_owned_field uuid,
9
+ data json
10
+ )
11
+ RETURNS void
12
+ AS $$
13
+ DECLARE
14
+ rec secrets_schema.secrets_table;
15
+ _sql text;
16
+ key text;
17
+
18
+ fields text[] = ARRAY[]::text[];
19
+ values text[] = ARRAY[]::text[];
20
+ pairs text[] = ARRAY[]::text[];
21
+ BEGIN
22
+
23
+ SELECT * FROM secrets_schema.secrets_table s
24
+ WHERE s.secrets_owned_field = secrets_table_upsert.secrets_owned_field
25
+ INTO rec;
26
+
27
+ IF (FOUND) THEN
28
+
29
+ FOR key IN SELECT json_object_keys(data)
30
+ LOOP
31
+ pairs = array_append(pairs, format('%s=%s', key, quote_literal(data->>key)));
32
+ END LOOP;
33
+
34
+ _sql = 'UPDATE secrets_schema.secrets_table SET '; -- it's already quoted! look at I above...
35
+ _sql = _sql || format('%s', array_to_string(pairs, ','));
36
+ _sql = _sql || ' WHERE secrets_owned_field=';
37
+ _sql = _sql || quote_literal(secrets_owned_field);
38
+ _sql = _sql || ';';
39
+
40
+ ELSE
41
+
42
+ values = array_append(values, quote_literal(secrets_owned_field));
43
+ fields = array_append(fields, 'secrets_owned_field');
44
+
45
+ FOR key IN SELECT json_object_keys(data)
46
+ LOOP
47
+ values = array_append(values, quote_literal(data->>key));
48
+ fields = array_append(fields, key);
49
+ END LOOP;
50
+
51
+ _sql = 'INSERT INTO secrets_schema.secrets_table ('; -- it's already quoted! look at I above...
52
+ _sql = _sql || format('%s)', array_to_string(fields, ','));
53
+ _sql = _sql || ' VALUES (';
54
+ _sql = _sql || format('%s)', array_to_string(values, ','));
55
+ _sql = _sql || ';';
56
+
57
+ END IF;
58
+
59
+ EXECUTE _sql;
60
+
61
+ END;
62
+ $$
63
+ LANGUAGE 'plpgsql' VOLATILE;
64
+
65
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_table_upsert TO authenticated;
66
+
67
+ COMMIT;
@@ -0,0 +1,33 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/secrets_upsert to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.secrets_upsert(
8
+ v_secrets_owned_field uuid,
9
+ secret_name text,
10
+ secret_value text,
11
+ field_encoding text = 'pgp'
12
+ )
13
+ RETURNS boolean
14
+ AS $$
15
+ BEGIN
16
+ INSERT INTO secrets_schema.secrets_table (secrets_owned_field, name, secrets_value_field, secrets_enc_field)
17
+ VALUES (v_secrets_owned_field, secrets_upsert.secret_name, secrets_upsert.secret_value::bytea, secrets_upsert.field_encoding)
18
+ ON CONFLICT (secrets_owned_field, name)
19
+ DO
20
+ UPDATE
21
+ SET
22
+ -- don't change this, cannot use EXCLUDED, don't know why, but you have to set to the ::bytea
23
+ secrets_value_field = secrets_upsert.secret_value::bytea,
24
+ secrets_enc_field = EXCLUDED.secrets_enc_field;
25
+ RETURN TRUE;
26
+ END
27
+ $$
28
+ LANGUAGE 'plpgsql'
29
+ VOLATILE;
30
+
31
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_upsert TO authenticated;
32
+
33
+ COMMIT;
@@ -0,0 +1,46 @@
1
+ -- Deploy schemas/encrypted_secrets/procedures/secrets_verify to pg
2
+
3
+ -- requires: schemas/encrypted_secrets/schema
4
+
5
+ BEGIN;
6
+
7
+ CREATE FUNCTION encrypted_secrets.secrets_verify(
8
+ secrets_owned_field uuid,
9
+ secret_name text,
10
+ secret_value text
11
+ )
12
+ RETURNS boolean
13
+ AS $$
14
+ DECLARE
15
+ v_secret_text text;
16
+ v_secret secrets_schema.secrets_table;
17
+ BEGIN
18
+ SELECT
19
+ *
20
+ FROM
21
+ encrypted_secrets.secrets_getter (secrets_verify.secrets_owned_field, secrets_verify.secret_name)
22
+ INTO v_secret_text;
23
+
24
+ SELECT
25
+ *
26
+ FROM
27
+ secrets_schema.secrets_table s
28
+ WHERE
29
+ s.name = secrets_verify.secret_name
30
+ AND s.secrets_owned_field = secrets_verify.secrets_owned_field INTO v_secret;
31
+
32
+ IF (v_secret.secrets_enc_field = 'crypt') THEN
33
+ RETURN v_secret_text = crypt(secrets_verify.secret_value::bytea::text, v_secret_text);
34
+ ELSIF (v_secret.secrets_enc_field = 'pgp') THEN
35
+ RETURN secrets_verify.secret_value = v_secret_text;
36
+ END IF;
37
+
38
+ RETURN secrets_verify.secret_value = v_secret_text;
39
+ END
40
+ $$
41
+ LANGUAGE 'plpgsql'
42
+ STABLE;
43
+
44
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_verify TO authenticated;
45
+
46
+ COMMIT;
@@ -0,0 +1,8 @@
1
+ -- Deploy schemas/encrypted_secrets/schema to pg
2
+
3
+
4
+ BEGIN;
5
+
6
+ CREATE SCHEMA encrypted_secrets;
7
+
8
+ COMMIT;
package/jest.config.js ADDED
@@ -0,0 +1,15 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+
6
+ // Match both __tests__ and colocated test files
7
+ testMatch: ['**/?(*.)+(test|spec).{ts,tsx,js,jsx}'],
8
+
9
+ // Ignore build artifacts and type declarations
10
+ testPathIgnorePatterns: ['/dist/', '\\.d\\.ts$'],
11
+ modulePathIgnorePatterns: ['<rootDir>/dist/'],
12
+ watchPathIgnorePatterns: ['/dist/'],
13
+
14
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
15
+ };
@@ -0,0 +1,8 @@
1
+ # launchql-encrypted-secrets extension
2
+ comment = 'launchql-encrypted-secrets extension'
3
+ default_version = '0.1.0'
4
+ module_pathname = '$libdir/launchql-encrypted-secrets'
5
+ requires = 'pgcrypto,plpgsql,uuid-ossp,launchql-encrypted-secrets-table,launchql-default-roles,launchql-verify'
6
+ relocatable = false
7
+ superuser = false
8
+
package/launchql.plan ADDED
@@ -0,0 +1,17 @@
1
+ %syntax-version=1.0.0
2
+ %project=launchql-encrypted-secrets
3
+ %uri=launchql-encrypted-secrets
4
+
5
+ schemas/encrypted_secrets/schema 2020-11-01T21:22:30Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/schema
6
+ schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text [schemas/encrypted_secrets/schema] 2020-11-01T21:22:54Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text
7
+ schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify [schemas/encrypted_secrets/schema] 2020-11-01T21:23:11Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify
8
+ schemas/encrypted_secrets/procedures/encrypt_field_crypt [schemas/encrypted_secrets/schema] 2020-11-01T21:23:19Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_crypt
9
+ schemas/encrypted_secrets/procedures/encrypt_field_pgp_get [schemas/encrypted_secrets/schema] 2020-11-01T21:23:29Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_pgp_get
10
+ schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter [schemas/encrypted_secrets/schema] 2020-11-01T21:23:36Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter
11
+ schemas/encrypted_secrets/procedures/encrypt_field_pgp [schemas/encrypted_secrets/schema] 2020-11-01T21:23:45Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_pgp
12
+ schemas/encrypted_secrets/procedures/encrypt_field_set [schemas/encrypted_secrets/schema] 2020-11-01T21:24:05Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/encrypt_field_set
13
+ schemas/encrypted_secrets/procedures/secrets_getter [schemas/encrypted_secrets/schema] 2020-11-01T21:24:23Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/secrets_getter
14
+ schemas/encrypted_secrets/procedures/secrets_upsert [schemas/encrypted_secrets/schema] 2020-11-01T21:24:35Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/secrets_upsert
15
+ schemas/encrypted_secrets/procedures/secrets_verify [schemas/encrypted_secrets/schema] 2020-11-01T21:24:41Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/secrets_verify
16
+ schemas/encrypted_secrets/procedures/secrets_table_upsert [schemas/encrypted_secrets/schema] 2020-11-01T21:25:00Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/secrets_table_upsert
17
+ schemas/encrypted_secrets/procedures/secrets_delete [schemas/encrypted_secrets/schema] 2020-11-01T21:31:24Z Dan Lynch <dlynch@Dans-MBP-3> # add schemas/encrypted_secrets/procedures/secrets_delete
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@pgpm/encrypted-secrets",
3
+ "version": "0.4.0",
4
+ "description": "Encrypted secrets management for PostgreSQL",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "scripts": {
9
+ "bundle": "lql package",
10
+ "test": "jest",
11
+ "test:watch": "jest --watch"
12
+ },
13
+ "dependencies": {
14
+ "@pgpm/default-roles": "0.4.0",
15
+ "@pgpm/encrypted-secrets-table": "0.4.0",
16
+ "@pgpm/verify": "0.4.0"
17
+ },
18
+ "devDependencies": {
19
+ "@launchql/cli": "^4.9.0"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/launchql/extensions"
24
+ },
25
+ "homepage": "https://github.com/launchql/extensions",
26
+ "bugs": {
27
+ "url": "https://github.com/launchql/extensions/issues"
28
+ },
29
+ "gitHead": "cc9f52a335caa6e21ee7751b04b77c84ce6cb809"
30
+ }
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_bytea_to_text;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_crypt from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_crypt;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_crypt_verify;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_pgp from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_pgp;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_pgp_get from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_pgp_get;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_pgp_getter;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/encrypt_field_set from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.encrypt_field_set;
6
+
7
+ COMMIT;
@@ -0,0 +1,8 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/secrets_delete from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.secrets_delete(uuid, text);
6
+ DROP FUNCTION encrypted_secrets.secrets_delete(uuid, text[]);
7
+
8
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/secrets_getter from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.secrets_getter;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/secrets_table_upsert from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.secrets_table_upsert;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/secrets_upsert from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.secrets_upsert;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/procedures/secrets_verify from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION encrypted_secrets.secrets_verify;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/encrypted_secrets/schema from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP SCHEMA encrypted_secrets;
6
+
7
+ COMMIT;
@@ -0,0 +1,231 @@
1
+ \echo Use "CREATE EXTENSION launchql-encrypted-secrets" to load this file. \quit
2
+ CREATE SCHEMA encrypted_secrets;
3
+
4
+ CREATE FUNCTION encrypted_secrets.encrypt_field_bytea_to_text(secret_value bytea) RETURNS text AS $EOFCODE$
5
+ SELECT
6
+ convert_from(encrypt_field_bytea_to_text.secret_value, 'SQL_ASCII');
7
+ $EOFCODE$ LANGUAGE sql IMMUTABLE;
8
+
9
+ CREATE FUNCTION encrypted_secrets.encrypt_field_crypt_verify(secrets_owned_field uuid, secret_value_field text, secret_verify_value text) RETURNS bool AS $EOFCODE$
10
+ DECLARE
11
+ result bool;
12
+ rec secrets_schema.secrets_table;
13
+ s_value text;
14
+ BEGIN
15
+
16
+ SELECT * FROM secrets_schema.secrets_table s
17
+ WHERE s.secrets_owned_field = encrypt_field_crypt_verify.secrets_owned_field
18
+ INTO rec;
19
+
20
+ EXECUTE format('SELECT ($1).%s::text', secret_value_field)
21
+ USING rec
22
+ INTO s_value;
23
+
24
+ SELECT
25
+ s_value = crypt(secret_verify_value, s_value)
26
+ INTO result;
27
+
28
+ RETURN result;
29
+ END;
30
+ $EOFCODE$ LANGUAGE plpgsql STABLE;
31
+
32
+ CREATE FUNCTION encrypted_secrets.encrypt_field_crypt() RETURNS trigger AS $EOFCODE$
33
+ BEGIN
34
+ NEW.field_name = crypt(NEW.field_name::text, gen_salt('bf'));
35
+ RETURN NEW;
36
+ END;
37
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
38
+
39
+ CREATE FUNCTION encrypted_secrets.encrypt_field_pgp_get(secret_value bytea, secret_encode text) RETURNS text AS $EOFCODE$
40
+ SELECT
41
+ convert_from(decode(pgp_sym_decrypt(encrypt_field_pgp_get.secret_value, encrypt_field_pgp_get.secret_encode), 'hex'), 'SQL_ASCII');
42
+ $EOFCODE$ LANGUAGE sql;
43
+
44
+ CREATE FUNCTION encrypted_secrets.encrypt_field_pgp_getter(secrets_owned_field uuid, secret_value_field text, secret_encode_field text) RETURNS text AS $EOFCODE$
45
+ DECLARE
46
+ result text;
47
+ rec secrets_schema.secrets_table;
48
+ s_value bytea;
49
+ s_enc text;
50
+ BEGIN
51
+
52
+ SELECT * FROM secrets_schema.secrets_table s
53
+ WHERE s.secrets_owned_field = encrypt_field_pgp_getter.secrets_owned_field
54
+ INTO rec;
55
+
56
+ EXECUTE format('SELECT ($1).%s::text', secret_value_field)
57
+ USING rec
58
+ INTO s_value;
59
+
60
+ EXECUTE format('SELECT ($1).%s::text', secret_encode_field)
61
+ USING rec
62
+ INTO s_enc;
63
+
64
+ SELECT
65
+ convert_from(decode(pgp_sym_decrypt(s_value, s_enc), 'hex'), 'SQL_ASCII')
66
+ INTO result;
67
+
68
+ RETURN result;
69
+ END;
70
+ $EOFCODE$ LANGUAGE plpgsql STABLE;
71
+
72
+ CREATE FUNCTION encrypted_secrets.encrypt_field_pgp() RETURNS trigger AS $EOFCODE$
73
+ BEGIN
74
+ NEW.field_name = pgp_sym_encrypt(encode(NEW.field_name::bytea, 'hex'), NEW.encode_field::text, 'compress-algo=1, cipher-algo=aes256');
75
+ RETURN NEW;
76
+ END;
77
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
78
+
79
+ CREATE FUNCTION encrypted_secrets.encrypt_field_set(secret_value text) RETURNS bytea AS $EOFCODE$
80
+ SELECT encrypt_field_set.secret_value::bytea;
81
+ $EOFCODE$ LANGUAGE sql;
82
+
83
+ CREATE FUNCTION encrypted_secrets.secrets_getter(secrets_owned_field uuid, secret_name text, default_value text DEFAULT NULL) RETURNS text AS $EOFCODE$
84
+ DECLARE
85
+ v_secret secrets_schema.secrets_table;
86
+ BEGIN
87
+ SELECT
88
+ *
89
+ FROM
90
+ secrets_schema.secrets_table s
91
+ WHERE
92
+ s.name = secrets_getter.secret_name
93
+ AND s.secrets_owned_field = secrets_getter.secrets_owned_field
94
+ INTO v_secret;
95
+
96
+ IF (NOT FOUND OR v_secret IS NULL) THEN
97
+ RETURN secrets_getter.default_value;
98
+ END IF;
99
+
100
+ IF (v_secret.secrets_enc_field = 'crypt') THEN
101
+ RETURN convert_from(v_secret.secrets_value_field, 'SQL_ASCII');
102
+ ELSIF (v_secret.secrets_enc_field = 'pgp') THEN
103
+ RETURN convert_from(decode(pgp_sym_decrypt(v_secret.secrets_value_field, v_secret.secrets_owned_field::text), 'hex'), 'SQL_ASCII');
104
+ END IF;
105
+
106
+ RETURN convert_from(v_secret.secrets_value_field, 'SQL_ASCII');
107
+
108
+ END
109
+ $EOFCODE$ LANGUAGE plpgsql STABLE;
110
+
111
+ CREATE FUNCTION encrypted_secrets.secrets_upsert(v_secrets_owned_field uuid, secret_name text, secret_value text, field_encoding text DEFAULT 'pgp') RETURNS boolean AS $EOFCODE$
112
+ BEGIN
113
+ INSERT INTO secrets_schema.secrets_table (secrets_owned_field, name, secrets_value_field, secrets_enc_field)
114
+ VALUES (v_secrets_owned_field, secrets_upsert.secret_name, secrets_upsert.secret_value::bytea, secrets_upsert.field_encoding)
115
+ ON CONFLICT (secrets_owned_field, name)
116
+ DO
117
+ UPDATE
118
+ SET
119
+ -- don't change this, cannot use EXCLUDED, don't know why, but you have to set to the ::bytea
120
+ secrets_value_field = secrets_upsert.secret_value::bytea,
121
+ secrets_enc_field = EXCLUDED.secrets_enc_field;
122
+ RETURN TRUE;
123
+ END
124
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
125
+
126
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_upsert TO authenticated;
127
+
128
+ CREATE FUNCTION encrypted_secrets.secrets_verify(secrets_owned_field uuid, secret_name text, secret_value text) RETURNS boolean AS $EOFCODE$
129
+ DECLARE
130
+ v_secret_text text;
131
+ v_secret secrets_schema.secrets_table;
132
+ BEGIN
133
+ SELECT
134
+ *
135
+ FROM
136
+ encrypted_secrets.secrets_getter (secrets_verify.secrets_owned_field, secrets_verify.secret_name)
137
+ INTO v_secret_text;
138
+
139
+ SELECT
140
+ *
141
+ FROM
142
+ secrets_schema.secrets_table s
143
+ WHERE
144
+ s.name = secrets_verify.secret_name
145
+ AND s.secrets_owned_field = secrets_verify.secrets_owned_field INTO v_secret;
146
+
147
+ IF (v_secret.secrets_enc_field = 'crypt') THEN
148
+ RETURN v_secret_text = crypt(secrets_verify.secret_value::bytea::text, v_secret_text);
149
+ ELSIF (v_secret.secrets_enc_field = 'pgp') THEN
150
+ RETURN secrets_verify.secret_value = v_secret_text;
151
+ END IF;
152
+
153
+ RETURN secrets_verify.secret_value = v_secret_text;
154
+ END
155
+ $EOFCODE$ LANGUAGE plpgsql STABLE;
156
+
157
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_verify TO authenticated;
158
+
159
+ CREATE FUNCTION encrypted_secrets.secrets_table_upsert(secrets_owned_field uuid, data pg_catalog.json) RETURNS void AS $EOFCODE$
160
+ DECLARE
161
+ rec secrets_schema.secrets_table;
162
+ _sql text;
163
+ key text;
164
+
165
+ fields text[] = ARRAY[]::text[];
166
+ values text[] = ARRAY[]::text[];
167
+ pairs text[] = ARRAY[]::text[];
168
+ BEGIN
169
+
170
+ SELECT * FROM secrets_schema.secrets_table s
171
+ WHERE s.secrets_owned_field = secrets_table_upsert.secrets_owned_field
172
+ INTO rec;
173
+
174
+ IF (FOUND) THEN
175
+
176
+ FOR key IN SELECT json_object_keys(data)
177
+ LOOP
178
+ pairs = array_append(pairs, format('%s=%s', key, quote_literal(data->>key)));
179
+ END LOOP;
180
+
181
+ _sql = 'UPDATE secrets_schema.secrets_table SET '; -- it's already quoted! look at I above...
182
+ _sql = _sql || format('%s', array_to_string(pairs, ','));
183
+ _sql = _sql || ' WHERE secrets_owned_field=';
184
+ _sql = _sql || quote_literal(secrets_owned_field);
185
+ _sql = _sql || ';';
186
+
187
+ ELSE
188
+
189
+ values = array_append(values, quote_literal(secrets_owned_field));
190
+ fields = array_append(fields, 'secrets_owned_field');
191
+
192
+ FOR key IN SELECT json_object_keys(data)
193
+ LOOP
194
+ values = array_append(values, quote_literal(data->>key));
195
+ fields = array_append(fields, key);
196
+ END LOOP;
197
+
198
+ _sql = 'INSERT INTO secrets_schema.secrets_table ('; -- it's already quoted! look at I above...
199
+ _sql = _sql || format('%s)', array_to_string(fields, ','));
200
+ _sql = _sql || ' VALUES (';
201
+ _sql = _sql || format('%s)', array_to_string(values, ','));
202
+ _sql = _sql || ';';
203
+
204
+ END IF;
205
+
206
+ EXECUTE _sql;
207
+
208
+ END;
209
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
210
+
211
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_table_upsert TO authenticated;
212
+
213
+ CREATE FUNCTION encrypted_secrets.secrets_delete(secrets_owned_field uuid, secret_name text) RETURNS void AS $EOFCODE$
214
+ BEGIN
215
+ DELETE FROM secrets_schema.secrets_table s
216
+ WHERE s.secrets_owned_field = secrets_delete.secrets_owned_field
217
+ AND s.name = secrets_delete.secret_name;
218
+ END
219
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
220
+
221
+ CREATE FUNCTION encrypted_secrets.secrets_delete(secrets_owned_field uuid, secret_names text[]) RETURNS void AS $EOFCODE$
222
+ BEGIN
223
+ DELETE FROM secrets_schema.secrets_table s
224
+ WHERE s.secrets_owned_field = secrets_delete.secrets_owned_field
225
+ AND s.name = ANY(secrets_delete.secret_names);
226
+ END
227
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
228
+
229
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_delete(uuid, text) TO authenticated;
230
+
231
+ GRANT EXECUTE ON FUNCTION encrypted_secrets.secrets_delete(uuid, text[]) TO authenticated;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_bytea_to_text on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_bytea_to_text');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_crypt on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_crypt');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_crypt_verify on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_crypt_verify');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_pgp on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_pgp');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_pgp_get on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_pgp_get');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_pgp_getter on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_pgp_getter');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/encrypt_field_set on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.encrypt_field_set');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/secrets_delete on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.secrets_delete');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/secrets_getter on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.secrets_getter');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/secrets_table_upsert on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.secrets_table_upsert');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/secrets_upsert on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.secrets_upsert');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/procedures/secrets_verify on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('encrypted_secrets.secrets_verify');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/encrypted_secrets/schema on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_schema ('encrypted_secrets');
6
+
7
+ ROLLBACK;