diff --git a/Core/Configurations/ConfigurationManager/Class1.cs b/Core/Configurations/ConfigurationManager/ConfigurationExtensions.cs similarity index 100% rename from Core/Configurations/ConfigurationManager/Class1.cs rename to Core/Configurations/ConfigurationManager/ConfigurationExtensions.cs diff --git a/Core/Configurations/ConfigurationManager/ConfigurationRepository.cs b/Core/Configurations/ConfigurationManager/ConfigurationRepository.cs index 8d4286f..60a44a1 100644 --- a/Core/Configurations/ConfigurationManager/ConfigurationRepository.cs +++ b/Core/Configurations/ConfigurationManager/ConfigurationRepository.cs @@ -2,7 +2,7 @@ using System.Data; using Insight.Database; namespace Configuration.Core; - public class ConfigurationRepository : IConfigurationRepository +public class ConfigurationRepository : IConfigurationRepository { private readonly IDbConnection _connection; @@ -16,8 +16,8 @@ namespace Configuration.Core; const string sql = @" SELECT id, key, value, label, content_type, valid_from, expires_at, created_at, modified_at, etag FROM prod.app_configuration - WHERE (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP) - AND (valid_from IS NULL OR valid_from <= CURRENT_TIMESTAMP)"; + WHERE (expires_at IS NULL OR expires_at <= CURRENT_TIMESTAMP) + AND (valid_from IS NULL OR valid_from >= CURRENT_TIMESTAMP)"; return await _connection.QueryAsync(sql); } diff --git a/Core/Configurations/ConfigurationManager/JsonConfigurationSection.cs b/Core/Configurations/ConfigurationManager/JsonConfigurationSection.cs index 6abaf42..7ffd90b 100644 --- a/Core/Configurations/ConfigurationManager/JsonConfigurationSection.cs +++ b/Core/Configurations/ConfigurationManager/JsonConfigurationSection.cs @@ -1,56 +1,80 @@ using Newtonsoft.Json.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; -using System.Data; -namespace Configuration.Core; -public class JsonConfigurationSection : IConfigurationSection +namespace Configuration.Core { - private readonly JObject _data; - private readonly string _path; - - public JsonConfigurationSection(JObject data, string path) + public class JsonConfigurationSection : IConfigurationSection { - _data = data; - _path = path; - } + private readonly JObject _data; + private readonly string _path; + private readonly string _normalizedPath; - public string this[string key] - { - get => _data.SelectToken($"{_path.Replace(":", ".")}.{key.Replace(":", ".")}")?.ToString(); - set => throw new NotImplementedException(); - } - - public string Key => _path.Split(':').Last(); - public string Path => _path; - public string Value - { - get => _data.SelectToken(_path.Replace(":", "."))?.ToString(); - set => throw new NotImplementedException(); - } - - - public IConfigurationSection GetSection(string key) => - new JsonConfigurationSection(_data, string.IsNullOrEmpty(_path) ? key : $"{_path}:{key}"); - - public JToken GetToken() => _data.SelectToken(_path.Replace(":", ".")); - - public IEnumerable GetChildren() - { - var token = _data.SelectToken(_path.Replace(":", ".")); - if (token is JObject obj) + public JsonConfigurationSection(JObject data, string path) { - return obj.Properties() - .Select(p => new JsonConfigurationSection(_data, - string.IsNullOrEmpty(_path) ? p.Name : $"{_path}:{p.Name}")); + _data = data ?? throw new ArgumentNullException(nameof(data)); + _path = path ?? throw new ArgumentNullException(nameof(path)); + _normalizedPath = NormalizePath(_path); } - return Enumerable.Empty(); - } - public T Get() where T : class - { - var token = _data.SelectToken(_path.Replace(":", ".")); - return token?.ToObject(); - } - public IChangeToken GetReloadToken() => new ConfigurationReloadToken(); + public string this[string key] + { + get + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + + var token = _data.SelectToken($"{_normalizedPath}.{NormalizePath(key)}"); + return token?.ToString(); + } + set => throw new NotImplementedException("Setting values is not supported."); + } + + public string Key => _path.Split(':').Last(); + public string Path => _path; + + public string Value + { + get + { + var token = _data.SelectToken(_normalizedPath); + return token?.ToString(); + } + set => throw new NotImplementedException("Setting values is not supported."); + } + + public IConfigurationSection GetSection(string key) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + + return new JsonConfigurationSection(_data, string.IsNullOrEmpty(_path) ? key : $"{_path}:{key}"); + } + + public JToken GetToken() => _data.SelectToken(_normalizedPath); + + public IEnumerable GetChildren() + { + var token = _data.SelectToken(_normalizedPath); + if (token is JObject obj) + { + return obj.Properties() + .Select(p => new JsonConfigurationSection(_data, string.IsNullOrEmpty(_path) ? p.Name : $"{_path}:{p.Name}")); + } + return Enumerable.Empty(); + } + + public T Get() where T : class + { + var token = _data.SelectToken(_normalizedPath); + return token?.ToObject(); + } + + public IChangeToken GetReloadToken() => new ConfigurationReloadToken(); + + private static string NormalizePath(string path) + { + return path?.Replace(":", ".", StringComparison.Ordinal) ?? string.Empty; + } + } } \ No newline at end of file diff --git a/Core/Configurations/ConfigurationManager/KeyValueConfigurationBuilder.cs b/Core/Configurations/ConfigurationManager/KeyValueConfigurationBuilder.cs index 77e8214..9b636b0 100644 --- a/Core/Configurations/ConfigurationManager/KeyValueConfigurationBuilder.cs +++ b/Core/Configurations/ConfigurationManager/KeyValueConfigurationBuilder.cs @@ -2,55 +2,102 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Microsoft.Extensions.Configuration; -namespace Configuration.Core; - public class KeyValueConfigurationBuilder +namespace Configuration.Core { - private readonly IConfigurationRepository _repository; - private readonly JObject _rootObject = new(); - private ConfigurationReloadToken _reloadToken = new(); - private IConfiguration _configuration; - - public KeyValueConfigurationBuilder(IConfigurationRepository repository) + public class KeyValueConfigurationBuilder { - _repository = repository; - } + private readonly IConfigurationRepository _repository; + private readonly JObject _rootObject = new(); + private ConfigurationReloadToken _reloadToken = new(); + private IConfiguration _configuration; + private readonly object _configurationLock = new(); - public async Task LoadConfiguration() - { - var configurations = await _repository.GetActiveConfigurations(); - foreach (var config in configurations) + public KeyValueConfigurationBuilder(IConfigurationRepository repository) { - AddKeyValue(config.Key, config.Value); + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); } - OnReload(); - } - public void AddKeyValue(string key, string jsonValue) - { - var valueObject = JsonConvert.DeserializeObject(jsonValue); - var parts = key.Split(':'); - - JObject current = _rootObject; - for (int i = 0; i < parts.Length - 1; i++) + /// + /// Loads configurations from the repository and builds the configuration tree. + /// + public async Task LoadConfiguration() { - var part = parts[i]; - if (!current.ContainsKey(part)) + try { - current[part] = new JObject(); + var configurations = await _repository.GetActiveConfigurations(); + foreach (var config in configurations) + { + AddKeyValue(config.Key, config.Value); + } + OnReload(); + } + catch (Exception ex) + { + // Log the exception or handle it as needed + throw new InvalidOperationException("Failed to load configurations.", ex); } - current = (JObject)current[part]; } - current[parts[^1]] = valueObject; - } + /// + /// Adds a key-value pair to the configuration tree. + /// + /// The key to add. + /// The JSON value to add. + public void AddKeyValue(string key, string jsonValue) + { + if (string.IsNullOrEmpty(key)) + throw new ArgumentNullException(nameof(key)); + if (string.IsNullOrEmpty(jsonValue)) + throw new ArgumentNullException(nameof(jsonValue)); - private void OnReload() - { - var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken()); - previousToken.OnReload(); - _configuration = null; - } + try + { + var valueObject = JsonConvert.DeserializeObject(jsonValue); + var parts = key.Split(':'); - public IConfiguration Build() => - _configuration ??= new JsonConfiguration(_rootObject, _reloadToken); + JObject current = _rootObject; + for (int i = 0; i < parts.Length - 1; i++) + { + var part = parts[i]; + if (!current.ContainsKey(part)) + { + current[part] = new JObject(); + } + current = (JObject)current[part]; + } + + current[parts[^1]] = valueObject; + } + catch (JsonException ex) + { + throw new ArgumentException("Invalid JSON value.", nameof(jsonValue), ex); + } + } + + private void OnReload() + { + var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken()); + previousToken.OnReload(); + _configuration = null; // Reset the configuration to force a rebuild + } + + /// + /// Builds the configuration instance. + /// + /// The built instance. + public IConfiguration Build() + { + if (_configuration == null) + { + lock (_configurationLock) + { + if (_configuration == null) + { + _configuration = new JsonConfiguration(_rootObject, _reloadToken); + } + } + } + return _configuration; + } + } } \ No newline at end of file diff --git a/Database/AppConfigurationSystem/ConfigurationDatabaseSetup.cs b/Database/ConfigurationManagementSystem/ConfigurationDatabaseSetup.cs similarity index 83% rename from Database/AppConfigurationSystem/ConfigurationDatabaseSetup.cs rename to Database/ConfigurationManagementSystem/ConfigurationDatabaseSetup.cs index 5cb31bb..0dd449d 100644 --- a/Database/AppConfigurationSystem/ConfigurationDatabaseSetup.cs +++ b/Database/ConfigurationManagementSystem/ConfigurationDatabaseSetup.cs @@ -11,11 +11,20 @@ public class ConfigurationDatabaseSetup { _connection = connection; } + public async Task CreateDatabaseStructure(IDbConnection connection) + { + await CreateConfigurationTable(); + await CreateHistoryTable(); + await CreateConfigurationIndexes(); + await CreateModifiedAtTrigger(); + await CreateNotifyTrigger(); + await CreateHistoryTrigger(); + } public async Task CreateConfigurationTable() { const string sql = @" - CREATE TABLE prod.app_configuration ( + CREATE TABLE app_configuration ( id bigserial NOT NULL, ""key"" varchar(255) NOT NULL, value text NULL, @@ -34,7 +43,7 @@ public class ConfigurationDatabaseSetup public async Task CreateHistoryTable() { const string sql = @" - CREATE TABLE prod.app_configuration_history ( + CREATE TABLE app_configuration_history ( history_id bigserial NOT NULL, action_type char(1) NOT NULL, action_timestamp timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -57,15 +66,15 @@ public class ConfigurationDatabaseSetup public async Task CreateConfigurationIndexes() { const string sql = @" - CREATE INDEX idx_app_configuration_key ON prod.app_configuration(""key""); - CREATE INDEX idx_app_configuration_validity ON prod.app_configuration(valid_from, expires_at);"; + CREATE INDEX idx_app_configuration_key ON app_configuration(""key""); + CREATE INDEX idx_app_configuration_validity ON app_configuration(valid_from, expires_at);"; await _connection.ExecuteAsync(sql); } public async Task CreateModifiedAtTrigger() { const string sql = @" - CREATE OR REPLACE FUNCTION prod.update_app_configuration_modified_at() + CREATE OR REPLACE FUNCTION update_app_configuration_modified_at() RETURNS TRIGGER AS $$ BEGIN NEW.modified_at = CURRENT_TIMESTAMP; @@ -74,16 +83,16 @@ public class ConfigurationDatabaseSetup $$ LANGUAGE plpgsql; CREATE TRIGGER trg_app_configuration_modified_at - BEFORE UPDATE ON prod.app_configuration + BEFORE UPDATE ON app_configuration FOR EACH ROW - EXECUTE FUNCTION prod.update_app_configuration_modified_at();"; + EXECUTE FUNCTION update_app_configuration_modified_at();"; await _connection.ExecuteAsync(sql); } public async Task CreateNotifyTrigger() { const string sql = @" - CREATE OR REPLACE FUNCTION prod.notify_app_configuration_change() + CREATE OR REPLACE FUNCTION notify_app_configuration_change() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('config_changes', NEW.key); @@ -92,20 +101,20 @@ public class ConfigurationDatabaseSetup $$ LANGUAGE plpgsql; CREATE TRIGGER trg_app_configuration_notify - AFTER INSERT OR UPDATE ON prod.app_configuration + AFTER INSERT OR UPDATE ON app_configuration FOR EACH ROW - EXECUTE FUNCTION prod.notify_app_configuration_change();"; + EXECUTE FUNCTION notify_app_configuration_change();"; await _connection.ExecuteAsync(sql); } public async Task CreateHistoryTrigger() { const string sql = @" - CREATE OR REPLACE FUNCTION prod.log_app_configuration_changes() + CREATE OR REPLACE FUNCTION log_app_configuration_changes() RETURNS TRIGGER AS $$ BEGIN IF (TG_OP = 'INSERT') THEN - INSERT INTO prod.app_configuration_history ( + INSERT INTO app_configuration_history ( action_type, id, ""key"", value, label, content_type, valid_from, expires_at, created_at, modified_at, etag ) @@ -114,7 +123,7 @@ public class ConfigurationDatabaseSetup NEW.valid_from, NEW.expires_at, NEW.created_at, NEW.modified_at, NEW.etag ); ELSIF (TG_OP = 'UPDATE') THEN - INSERT INTO prod.app_configuration_history ( + INSERT INTO app_configuration_history ( action_type, id, ""key"", value, label, content_type, valid_from, expires_at, created_at, modified_at, etag ) @@ -123,7 +132,7 @@ public class ConfigurationDatabaseSetup OLD.valid_from, OLD.expires_at, OLD.created_at, OLD.modified_at, OLD.etag ); ELSIF (TG_OP = 'DELETE') THEN - INSERT INTO prod.app_configuration_history ( + INSERT INTO app_configuration_history ( action_type, id, ""key"", value, label, content_type, valid_from, expires_at, created_at, modified_at, etag ) @@ -137,18 +146,10 @@ public class ConfigurationDatabaseSetup $$ LANGUAGE plpgsql; CREATE TRIGGER trg_app_configuration_history - AFTER INSERT OR UPDATE OR DELETE ON prod.app_configuration - FOR EACH ROW EXECUTE FUNCTION prod.log_app_configuration_changes();"; + AFTER INSERT OR UPDATE OR DELETE ON app_configuration + FOR EACH ROW EXECUTE FUNCTION log_app_configuration_changes();"; await _connection.ExecuteAsync(sql); } - public async Task CreateDatabaseStructure(IDbConnection connection) - { - await CreateConfigurationTable(); - await CreateHistoryTable(); - await CreateConfigurationIndexes(); - await CreateModifiedAtTrigger(); - await CreateNotifyTrigger(); - await CreateHistoryTrigger(); - } + } diff --git a/Database/Database.csproj b/Database/Database.csproj index d737a20..8f3ffb3 100644 --- a/Database/Database.csproj +++ b/Database/Database.csproj @@ -9,4 +9,9 @@ + + + + + diff --git a/Database/Identity/DbInfrastructureSetup.cs b/Database/IdentitySystem/DbInfrastructureSetup.cs similarity index 100% rename from Database/Identity/DbInfrastructureSetup.cs rename to Database/IdentitySystem/DbInfrastructureSetup.cs diff --git a/Database/IdentitySystem/Setup.cs b/Database/IdentitySystem/Setup.cs new file mode 100644 index 0000000..518546e --- /dev/null +++ b/Database/IdentitySystem/Setup.cs @@ -0,0 +1,115 @@ +using System; +using System.Data; +using System.Threading.Tasks; + +namespace Database.Tenants +{ + public class DbSetup + { + private readonly IDbConnection _db; + + public DbSetup(IDbConnection db) + { + _db = db ?? throw new ArgumentNullException(nameof(db)); + } + + /// + /// Creates the users table in the ptmain schema. + /// + public void CreateUsersTable() + { + ExecuteInTransaction(@" + CREATE TABLE IF NOT EXISTS ptmain.users ( + id SERIAL PRIMARY KEY, + email VARCHAR(256) NOT NULL UNIQUE, + password_hash VARCHAR(256) NOT NULL, + security_stamp VARCHAR(36) NOT NULL, + email_confirmed BOOLEAN NOT NULL DEFAULT FALSE, + access_failed_count INTEGER NOT NULL DEFAULT 0, + lockout_enabled BOOLEAN NOT NULL DEFAULT TRUE, + lockout_end TIMESTAMPTZ NULL, + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_login_at TIMESTAMPTZ NULL + );"); + } + + /// + /// Creates the tenants table in the ptmain schema. + /// + public void CreateTenantsTable() + { + ExecuteInTransaction(@" + CREATE TABLE IF NOT EXISTS ptmain.tenants ( + id SERIAL PRIMARY KEY, + connection_string VARCHAR(500) NOT NULL, + is_active BOOLEAN NOT NULL DEFAULT TRUE, + created_by INTEGER NOT NULL REFERENCES ptmain.users(id), + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP + );"); + } + + /// + /// Creates the user_tenants table in the ptmain schema. + /// + public void CreateUserTenantsTable() + { + ExecuteInTransaction(@" + CREATE TABLE IF NOT EXISTS ptmain.user_tenants ( + user_id INTEGER NOT NULL REFERENCES ptmain.users(id), + tenant_id INTEGER NOT NULL REFERENCES ptmain.tenants(id), + pin_code VARCHAR(10) NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id, tenant_id) + );"); + } + + /// + /// Sets up Row Level Security (RLS) for the tenants and user_tenants tables. + /// + public void SetupRLS() + { + ExecuteInTransaction( + "ALTER TABLE ptmain.tenants ENABLE ROW LEVEL SECURITY;", + "ALTER TABLE ptmain.user_tenants ENABLE ROW LEVEL SECURITY;", + "DROP POLICY IF EXISTS tenant_access ON ptmain.tenants;", + @" + CREATE POLICY tenant_access ON ptmain.tenants + USING (id IN ( + SELECT tenant_id + FROM ptmain.user_tenants + WHERE user_id = current_setting('app.user_id', TRUE)::INTEGER + ));", + "DROP POLICY IF EXISTS user_tenant_access ON ptmain.user_tenants;", + @" + CREATE POLICY user_tenant_access ON ptmain.user_tenants + USING (user_id = current_setting('app.user_id', TRUE)::INTEGER);" + ); + } + + /// + /// Executes one or more SQL commands within a transaction. + /// + /// The SQL commands to execute. + private void ExecuteInTransaction(params string[] sqlCommands) + { + if (_db.State != ConnectionState.Open) + _db.Open(); + + using var transaction = _db.BeginTransaction(); + try + { + foreach (var sql in sqlCommands) + { + _db.ExecuteSql(sql, transaction: transaction); + } + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new InvalidOperationException("Failed to execute SQL commands in transaction.", ex); + } + } + } +} \ No newline at end of file diff --git a/Database/Identity/UserService.cs b/Database/IdentitySystem/UserService.cs similarity index 100% rename from Database/Identity/UserService.cs rename to Database/IdentitySystem/UserService.cs diff --git a/Database/RolesPermissionSystem/Setup.cs b/Database/RolesPermissionSystem/Setup.cs new file mode 100644 index 0000000..2370fde --- /dev/null +++ b/Database/RolesPermissionSystem/Setup.cs @@ -0,0 +1,101 @@ +using System.Data; +using System.Text.RegularExpressions; +using Insight.Database; + +namespace Database.Tenants +{ + public class Setup + { + private readonly IDbConnection _db; + + public Setup(IDbConnection db) + { + _db = db ?? throw new ArgumentNullException(nameof(db)); + } + + /// + /// Creates the system tables in the specified schema within a transaction. + /// + /// The schema name where the tables will be created. + public async Task CreateSystem(string schema) + { + if (!IsValidSchemaName(schema)) + throw new ArgumentException("Invalid schema name", nameof(schema)); + + using (var transaction = _db.BeginTransaction()) + { + try + { + await CreateRolesTable(schema, transaction).ConfigureAwait(false); + await CreatePermissionsTable(schema, transaction).ConfigureAwait(false); + await CreateRolePermissionsTable(schema, transaction).ConfigureAwait(false); + + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new InvalidOperationException("Failed to create system tables.", ex); + } + } + } + + private bool IsValidSchemaName(string schema) + { + return !string.IsNullOrEmpty(schema) && Regex.IsMatch(schema, "^[a-zA-Z0-9_]+$"); + } + + private async Task ExecuteSqlAsync(string sql, IDbTransaction transaction) + { + if (string.IsNullOrEmpty(sql)) + throw new ArgumentNullException(nameof(sql)); + + await _db.ExecuteAsync(sql, transaction: transaction).ConfigureAwait(false); + } + + private async Task CreatePermissionTypesTable(string schema, IDbTransaction transaction) + { + var sql = $@" + CREATE TABLE IF NOT EXISTS {schema}.permission_types ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL UNIQUE + )"; + await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); + } + + private async Task CreatePermissionsTable(string schema, IDbTransaction transaction) + { + var sql = $@" + CREATE TABLE IF NOT EXISTS {schema}.permissions ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL UNIQUE, + type_id INTEGER NOT NULL, + FOREIGN KEY (type_id) REFERENCES {schema}.permission_types(id) + )"; + await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); + } + + private async Task CreateRolesTable(string schema, IDbTransaction transaction) + { + var sql = $@" + CREATE TABLE IF NOT EXISTS {schema}.roles ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL UNIQUE + )"; + await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); + } + + private async Task CreateRolePermissionsTable(string schema, IDbTransaction transaction) + { + var sql = $@" + CREATE TABLE IF NOT EXISTS {schema}.role_permissions ( + role_id INTEGER NOT NULL, + permission_id INTEGER NOT NULL, + PRIMARY KEY (role_id, permission_id), + FOREIGN KEY (role_id) REFERENCES {schema}.roles(id), + FOREIGN KEY (permission_id) REFERENCES {schema}.permissions(id) + )"; + await ExecuteSqlAsync(sql, transaction).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/Database/Tenants/TenantData.cs b/Database/Tenants/InitializeTenantData.cs similarity index 98% rename from Database/Tenants/TenantData.cs rename to Database/Tenants/InitializeTenantData.cs index 93707bf..7322025 100644 --- a/Database/Tenants/TenantData.cs +++ b/Database/Tenants/InitializeTenantData.cs @@ -3,11 +3,11 @@ using System.Data; namespace Database.Tenants { - internal class TenantData + internal class InitializeTenantData { private readonly IDbConnection _db; - public TenantData(IDbConnection db) + public InitializeTenantData(IDbConnection db) { _db = db; } diff --git a/SqlManagement/.dbeaver/.project-metadata.json.bak b/SqlManagement/.dbeaver/.project-metadata.json.bak index 219bc44..c517be1 100644 --- a/SqlManagement/.dbeaver/.project-metadata.json.bak +++ b/SqlManagement/.dbeaver/.project-metadata.json.bak @@ -1 +1 @@ -{"resources":{"Scripts/grant-privileges.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"}}} \ No newline at end of file +{"resources":{"Scripts/Script-2.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"},"Scripts/grant-privileges.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"}}} \ No newline at end of file diff --git a/SqlManagement/.dbeaver/project-metadata.json b/SqlManagement/.dbeaver/project-metadata.json index c517be1..a63e45b 100644 --- a/SqlManagement/.dbeaver/project-metadata.json +++ b/SqlManagement/.dbeaver/project-metadata.json @@ -1 +1 @@ -{"resources":{"Scripts/Script-2.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"},"Scripts/grant-privileges.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"}}} \ No newline at end of file +{"resources":{"Scripts/Script-1.sql":{"default-datasource":"postgres-jdbc-19484872d85-cd2a4a40116e706","default-catalog":"ptdb01","default-schema":"ptmain"},"Scripts/Script-2.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"},"Scripts/grant-privileges.sql":{"default-datasource":"postgres-jdbc-1948450a8b4-5fc9eec404e65c44","default-catalog":"ptdb01"}}} \ No newline at end of file diff --git a/SqlManagement/Scripts/Script-1.sql b/SqlManagement/Scripts/Script-1.sql new file mode 100644 index 0000000..e69de29 diff --git a/SqlManagement/Scripts/grant-privileges.sql b/SqlManagement/Scripts/grant-privileges.sql index d9ed48d..453ca63 100644 --- a/SqlManagement/Scripts/grant-privileges.sql +++ b/SqlManagement/Scripts/grant-privileges.sql @@ -8,6 +8,7 @@ CREATE SCHEMA ptmain; GRANT USAGE, CREATE ON SCHEMA ptmain TO sathumper; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA ptmain TO sathumper; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA ptmain TO sathumper; + ALTER DEFAULT PRIVILEGES IN SCHEMA ptmain GRANT ALL PRIVILEGES ON TABLES TO sathumper; diff --git a/Tests/TextFile1.txt b/Tests/TextFile1.txt deleted file mode 100644 index 61a436b..0000000 --- a/Tests/TextFile1.txt +++ /dev/null @@ -1,96 +0,0 @@ - -GRANT USAGE, CREATE ON SCHEMA swp TO sathumper; - -ALTER DEFAULT PRIVILEGES IN SCHEMA swp -GRANT ALL PRIVILEGES ON TABLES TO sathumper; - -GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA swp TO sathumper; - -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA swp TO sathumper; - - -select * from dev.app_configuration - -CREATE OR REPLACE FUNCTION dev.notify_config_change() -RETURNS TRIGGER AS $$ -BEGIN - IF TG_OP = 'DELETE' THEN - PERFORM pg_notify( - 'config_changes', - json_build_object('operation', TG_OP, 'data', row_to_json(OLD))::text - ); - RETURN OLD; -- Return OLD for DELETE operations - ELSE - PERFORM pg_notify( - 'config_changes', - json_build_object('operation', TG_OP, 'data', row_to_json(NEW))::text - ); - RETURN NEW; -- Return NEW for INSERT/UPDATE operations - END IF; -END; -$$ LANGUAGE plpgsql; - - - --- Trigger på configuration tabellen -CREATE TRIGGER config_change_trigger -AFTER INSERT OR UPDATE OR DELETE ON dev.app_configuration -FOR EACH ROW EXECUTE FUNCTION dev.notify_config_change(); - - -update dev.app_configuration -set "label" = "label" where id = 3 - -SELECT row_to_json(t) -FROM (SELECT 1 as id, 'test' as key) t; - -SET myapp.tenant_id = '1'; - -SHOW myapp.tenant_id; - -create TABLE dev.app_configuration1 ( - tenant_id varchar(25), - config_key VARCHAR(255), - config_value TEXT, - PRIMARY KEY (tenant_id, config_key) -); - -ALTER TABLE dev.app_configuration1 ENABLE ROW LEVEL SECURITY; - -CREATE drop POLICY tenant_policy -ON dev.app_configuration1 -FOR SELECT -USING ( - current_setting('myapp.tenant_id', true) IS NOT NULL AND - tenant_id = current_setting('myapp.tenant_id')::INT -); - -CREATE POLICY tenant_policy -ON dev.app_configuration1 -FOR SELECT -USING ( - tenant_id::text = current_user -); - -insert into dev.app_configuration1 -(tenant_id, config_key, config_value) values('dev', 't2', 'best dat') - -SET myapp.tenant_id = 0 - -ALTER USER din_bruger NOBYPASS RLS; -select * from dev.app_configuration1 - -SHOW row_security; - - - - -GRANT USAGE, CREATE ON SCHEMA dev TO sathumper; - -ALTER DEFAULT PRIVILEGES IN SCHEMA dev -GRANT ALL PRIVILEGES ON TABLES TO sathumper; - -GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA dev TO sathumper; - -GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA dev TO sathumper; -