Skip to content

Business rules

DBConnectionAPI composes database connection strings for authorized callers from templates and secure credentials held in the SAIF Environment API, validates Oracle and SQL Server strings with a connectivity probe, caches validated strings in memory, and exposes an Admin surface that returns the raw credential parts. The 25 rules below cover caching, key construction, template and credential handling, probe and retry behavior, error-to-status mapping, audit logging, Admin disclosure and authorization. Rules are numbered BR-DBC-001 to BR-DBC-025. Each rule heading combines the rule identifier and its title.


BR-DBC-001 Serve from the cache when the key is present

Field Value
Title Serve from the cache when the key is present
Statement On every retrieval request the Environment API template is read and formatted first. A connection string is then served from the in-memory cache when its key is already present. Secure-credential retrieval, credential validation, the connectivity probe and cache insertion run only on a cache miss
Trigger Every retrieval request, in the service layer after the template lookup and before the secure-credential retrieval
Enforcement ConnectionManager.GetOracleConnectionString, GetSqlConnectionString, GetPostgreSqlConnectionString (HasConnection, GetCachedConnection)
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome Cache hit: the template lookup still occurs, but secure-credential retrieval, credential validation, the connectivity probe and cache insertion are skipped; the stored string is returned with status 200
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:52-87

BR-DBC-002 Cache key format

Field Value
Title Cache key format
Statement Cache keys are ORA.<instance>.<user>, SQL.<server>.<instance>.<user> and POSTGRES.<server>.<instance>.<user>. They are case-sensitive and not normalized
Trigger Key generation at the start of each retrieval request
Enforcement ConnectionManager key builders
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome Requests that differ only in letter case produce separate cache entries and separate probes
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:88-98

BR-DBC-003 Guarded cache insert, first writer wins

Field Value
Title Guarded cache insert, first writer wins
Statement A value is added to the cache only under the lock, only when it is not null or whitespace, and only when the key is absent
Trigger After a successful validation
Enforcement ConnectionManager.AddConnectionString under lock (_connectionsLock)
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome Duplicates and blank values are ignored; concurrent misses on one key cost redundant work but cannot corrupt the cache
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:37-50

BR-DBC-004 Cache entries never expire

Field Value
Title Cache entries never expire
Statement Cached entries persist for the lifetime of the singleton. There is no expiry, eviction or invalidation
Trigger Process lifetime
Enforcement IConnectionManager registered as a singleton holding a process-wide dictionary
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome A credential rotation takes effect only after the process restarts
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:16; DBConnectionAPI/DBConnectionAPI/Startup.cs:23

BR-DBC-005 Secure-setting keys are lower-cased

Field Value
Title Secure-setting keys are lower-cased
Statement Secure-setting key names are lower-cased: ora_<instance>_<user>, sql_<server>_<instance>_<user> and postgres_<instance>_<user>
Trigger Retrieval and Admin requests, when the credential key is built
Enforcement ConnectionManager secure-setting key builders (used by AdminController through IConnectionManager)
Origin api
Reached from All six operations: GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}, POST /v1/Admin/GetOracleConnectionInfo, POST /v1/Admin/GetPostgresConnectionInfo, POST /v1/Admin/GetSqlConnectionInfo
Outcome Credential lookup is case-insensitive for the caller-supplied parts, while the cache key is not (see BR-DBC-002)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:100-111

BR-DBC-006 PostgreSQL credentials ignore the server

Field Value
Title PostgreSQL credentials ignore the server
Statement PostgreSQL credentials are keyed by instance and user only. The cache key does include the server
Trigger PostgreSQL retrieval and Admin requests
Enforcement ConnectionManager PostgreSQL key builders
Origin api
Reached from GET /v1/Connections/PostgreSQL/{instance}/{username}, POST /v1/Admin/GetPostgresConnectionInfo
Outcome One credential is shared across all servers for an instance and user, while each server still gets its own cache entry
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:96-98,108-111

BR-DBC-007 Templates come from the Environment API group

Field Value
Title Templates come from the Environment API group
Statement Connection-string templates are read from Environment API group dbconnectionapi (keys sqlconnectiontemplate, postgresqlconnectiontemplate, oraconnectiontemplate). {SERVER} and {INSTANCE} are replaced; the Oracle template replaces only {INSTANCE}
Trigger Template retrieval for every operation
Enforcement ConnectionManager.GetSqlTemplate, GetPostgreSqlTemplate, GetOracleTemplate; key names in EnvironmentKeys
Origin api
Reached from All six operations: GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}, POST /v1/Admin/GetOracleConnectionInfo, POST /v1/Admin/GetPostgresConnectionInfo, POST /v1/Admin/GetSqlConnectionInfo
Outcome A template with placeholders substituted. There is no null guard, so a missing template fails with an unhandled null reference (status not visible in source**)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:119-133; DBConnectionAPI/DBConnectionAPI/EnvironmentKeys.cs:10-13

BR-DBC-008 Credentials are substituted into the template

Field Value
Title Credentials are substituted into the template
Statement The user name and password are substituted by replacing {USER} and {PW} in the template
Trigger Formatting, after the credential has been read
Enforcement ConnectionManager.FormatConnectionString
Origin api
Reached from All six operations (see BR-DBC-005 for the endpoint list)
Outcome The composed connection string
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:134

BR-DBC-009 Missing secure setting returns 404

Field Value
Title Missing secure setting returns 404
Statement If the secure setting does not exist, the request fails with HTTP 404. The message names the group, the key and the Environment API track
Trigger HasSetting returns false
Enforcement ConnectionManager.RetrieveConnection, throwing ConnectionManagerException(404)
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome 404 produced by the package error-handling middleware (DynamicErrorHandler†) from the typed exception; the message discloses the key name and track
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:141,189; DBConnectionAPI/DBConnectionAPI/Exceptions/ConnectionManagerException.cs:9-12

BR-DBC-010 Credential split at the first pipe, unguarded

Field Value
Title Credential split at the first pipe, unguarded
Statement The secure credential is split at the first pipe character: the user name is before it and the password after it. No guard exists
Trigger Retrieval after HasSetting succeeds
Enforcement ConnectionManager.RetrieveConnection
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome A missing pipe raises ArgumentOutOfRangeException outside the retry try; it is not a ConnectionManagerException, so the status depends on the package middleware (presumably 500**)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:145-148

BR-DBC-011 Backoff list must cover the retry attempts

Field Value
Title Backoff list must cover the retry attempts
Statement The backoff delay list must have at least MaxConnectionTestAttempts - 1 entries
Trigger Before probing
Enforcement ConnectionManager.ValidateBackoffConfiguration
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome Otherwise InvalidOperationException (a configuration guard against a subclass change; status not visible in source**)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:222-230

BR-DBC-012 Four probe attempts with 2, 3 and 5 second backoff

Field Value
Title Four probe attempts with 2, 3 and 5 second backoff
Statement The connectivity probe is attempted up to 4 times, waiting 2 s, 3 s and 5 s between attempts. The wait is a blocking delay
Trigger Probe failure
Enforcement ConnectionManager.RetrieveConnection retry loop; delay helper using Thread.Sleep
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome Transient failures recover; a failing key adds up to about 10 s of latency on the request thread. PostgreSQL never fails the probe (see BR-DBC-020)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:153-185,197-215

BR-DBC-013 Permanent errors stop retrying

Field Value
Title Permanent errors stop retrying
Statement Retrying stops immediately for ORA-12154, ORA-12504, TNS-12154, ORA-01017, Login failed and ORA-01000
Trigger Exception message match inside the retry loop
Enforcement ConnectionManager.RetrieveConnection (permanent-error check)
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome The loop breaks and the final error is mapped by BR-DBC-015 and BR-DBC-016
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:176-180,257-265

BR-DBC-014 A false probe result is a failure

Field Value
Title A false probe result is a failure
Statement A probe that returns false counts as a failure with status 500 ("did not return a successful result")
Trigger The probe delegate returns false
Enforcement ConnectionManager.RetrieveConnection
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome The attempt is retried; after the attempts run out the error is mapped (see BR-DBC-017)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:160

BR-DBC-015 TNS alias errors map to 400

Field Value
Title TNS alias errors map to 400
Statement TNS alias errors (ORA-12154, ORA-12504, TNS-12154) map to HTTP 400. The message cites the alias and the track
Trigger Final failure after the retry loop
Enforcement ConnectionManager.BuildConnectionManagerException
Origin api
Reached from GET /v1/Connections/ORA/{instance}/{username}; possible from GET /v1/Connections/SQL/{server}/{instance}/{username}*
Outcome ConnectionManagerException(400) rendered by the package middleware (DynamicErrorHandler†)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:232-255

BR-DBC-016 Other final failures map to 500

Field Value
Title Other final failures map to 500
Statement Other final failures map to HTTP 500: ORA-01034 (database unavailable), ORA-01000 (cursors), ORA-01017 or Login failed (invalid credentials; the message names the secure-setting key and track), ORA-12570, ORA-12571 and ORA-03135 (network interrupted; includes the raw message), and a generic fallback (Something went wrong, then See: and the raw message {msg})
Trigger Final failure after the retry loop
Enforcement ConnectionManager.BuildConnectionManagerException
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome ConnectionManagerException(500) rendered by the package middleware (DynamicErrorHandler†); the body can disclose key names, the track and raw provider messages
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:232-255

BR-DBC-017 An existing typed exception keeps its status

Field Value
Title An existing typed exception keeps its status
Statement If the last exception is already a ConnectionManagerException, it is rethrown unchanged and its status is kept
Trigger Final failure after the retry loop
Enforcement ConnectionManager.BuildConnectionManagerException
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome For example, a probe that returned false stays a 500 (see BR-DBC-014)
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:232-255

BR-DBC-018 Probe validity definition

Field Value
Title Probe validity definition
Statement Oracle validity means SELECT 1 FROM DUAL returns non-null. SQL Server validity means SELECT 1 returns non-null. A null unit of work means invalid
Trigger Each probe attempt
Enforcement ConnectionManager.TestOracleConnection, TestSqlConnection through IUnitOfWorkFactory (SAIF.NetCore.NHibernate)
Origin api
Reached from GET /v1/Connections/ORA/{instance}/{username}, GET /v1/Connections/SQL/{server}/{instance}/{username}
Outcome A boolean validity result; the scalar value itself is never inspected
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:267-305

BR-DBC-019 Legacy token decoded for the probe only

Field Value
Title Legacy token decoded for the probe only
Statement Before probing, the legacy token ZSPECIALCHAR is replaced with the BEL control character (char 7). This applies only to the string under test; the cached and returned string keeps the token
Trigger Each probe attempt
Enforcement ConnectionManager.HandleLegacyConnectionString
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome The probe connects with the decoded string; callers receive the raw token and must decode it themselves*
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:157,312-316

BR-DBC-020 PostgreSQL strings are never probed

Field Value
Title PostgreSQL strings are never probed
Statement PostgreSQL connection strings are never probed. The test always returns true
Trigger PostgreSQL retrieval on a cache miss
Enforcement ConnectionManager.TestPostgreSqlConnection
Origin api
Reached from GET /v1/Connections/PostgreSQL/{instance}/{username}
Outcome The string is cached and returned without validation
Source DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs:307-311

BR-DBC-021 Blank PostgreSQL server becomes a placeholder

Field Value
Title Blank PostgreSQL server becomes a placeholder
Statement When serverName is null or whitespace, the server value is set to the literal placeholder {SERVER}
Trigger PostgreSQL request without the optional serverName query value
Enforcement ConnectionsController.GetPostgreSqlConnectionString (action body)
Origin api
Reached from GET /v1/Connections/PostgreSQL/{instance}/{username}
Outcome The returned string and the cache key keep {SERVER} for the caller to fill in
Source DBConnectionAPI/DBConnectionAPI/Controllers/ConnectionsController.cs:40-41

BR-DBC-022 Retrieval requests are audit-logged

Field Value
Title Retrieval requests are audit-logged
Statement Every retrieval request is logged at Info level with the key components, the caller's UserId and AuthenticationScheme, and the timestamp. Admin requests are not logged
Trigger Each Connections request
Enforcement ConnectionsController actions through ISaifLogService and IIdentityServiceAccessor
Origin api
Reached from GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}
Outcome An audit trail for retrievals only; the identity is used for logging, never for an authorization decision
Source DBConnectionAPI/DBConnectionAPI/Controllers/ConnectionsController.cs:32,43,51

BR-DBC-023 Admin inspection discloses raw credentials

Field Value
Title Admin inspection discloses raw credentials
Statement Admin inspection reads the secure setting directly. It skips the HasSetting check, the probe and the cache, and returns the raw credentials, user, pw and connectionString
Trigger Each Admin request
Enforcement AdminController.GetConnectionInfo
Origin api
Reached from POST /v1/Admin/GetOracleConnectionInfo, POST /v1/Admin/GetPostgresConnectionInfo, POST /v1/Admin/GetSqlConnectionInfo
Outcome Plaintext credential disclosure with status 200. A missing secure setting is not handled locally, so the failure is raised by the package client and its status is not visible in source**
Source DBConnectionAPI/DBConnectionAPI/Controllers/AdminController.cs:48-70

BR-DBC-024 Admin split failure reported with status 200

Field Value
Title Admin split failure reported with status 200
Statement If the Admin credential split fails, the response adds an Error entry (pipe delimiter expected), returns an empty user and password, and keeps HTTP 200
Trigger Exception while splitting the credential
Enforcement Bare try/catch in AdminController.GetConnectionInfo
Origin api
Reached from POST /v1/Admin/GetOracleConnectionInfo, POST /v1/Admin/GetPostgresConnectionInfo, POST /v1/Admin/GetSqlConnectionInfo
Outcome The error is reported in the body while the status stays 200; the raw credentials value is still returned
Source DBConnectionAPI/DBConnectionAPI/Controllers/AdminController.cs:54-63

BR-DBC-025 Resource authorization on every endpoint

Field Value
Title Resource authorization on every endpoint
Statement All endpoints require resource DBConnAPIRetrieveResource. The caller identity name must be non-blank, the domain prefix is stripped, and the security service authorization check is called†
Trigger Every request, before the action runs
Enforcement [SecurityApiAuthorize("DBConnAPIRetrieveResource")] on both controllers; handler behavior from SAIF.NetCore.ServiceExtensions†
Origin inherited
Reached from All six operations: GET /v1/Connections/SQL/{server}/{instance}/{username}, GET /v1/Connections/PostgreSQL/{instance}/{username}, GET /v1/Connections/ORA/{instance}/{username}, POST /v1/Admin/GetOracleConnectionInfo, POST /v1/Admin/GetPostgresConnectionInfo, POST /v1/Admin/GetSqlConnectionInfo
Outcome Denied requests receive a status the package documentation does not state** (401 or 403 by ASP.NET Core convention*). The same resource guards the Admin and the retrieval endpoints, and no data-level check ties the caller to the requested instance or user
Source DBConnectionAPI/DBConnectionAPI/Controllers/ConnectionsController.cs:13; DBConnectionAPI/DBConnectionAPI/Controllers/AdminController.cs:12

Note: Statements marked with † are derived from dependency library documentation via the Legends CLI. Statements marked with * were inferred from code structure or contextual evidence. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • DBConnectionAPI/DBConnectionAPI/Services/ConnectionManager.cs
  • DBConnectionAPI/DBConnectionAPI/Services/IConnectionManager.cs
  • DBConnectionAPI/DBConnectionAPI/Controllers/ConnectionsController.cs
  • DBConnectionAPI/DBConnectionAPI/Controllers/AdminController.cs
  • DBConnectionAPI/DBConnectionAPI/Exceptions/ConnectionManagerException.cs
  • DBConnectionAPI/DBConnectionAPI/EnvironmentKeys.cs
  • DBConnectionAPI/DBConnectionAPI/Startup.cs