Skip to content

Commit dec850e

Browse files
Merge branch 'main' into cursor/mcp-ddl-schema-gate-c497
2 parents 6ac5807 + c8b0865 commit dec850e

15 files changed

Lines changed: 298 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,34 @@ for product tags (`vMAJOR.MINOR.PATCH`).
88

99
Product releases follow a **weekly cadence** (Saturday 09:00 America/Los_Angeles). See `docs/oss-ux/RELEASE.md`.
1010

11+
## [1.3.0] — 2026-08-24
12+
13+
### Added
14+
15+
- **DeepSQL Desktop** — first-ship Electron thin client (`desktop/`) with direct TLS and SSH-tunnel transports, connection profiles, OS keychain secrets, and native chrome (#73).
16+
- Desktop release workflow (`.github/workflows/desktop-release.yml`) for macOS / Windows / Linux installers on `desktop-v*` tags.
17+
- Enforceable Agent brain-note proposals with non-blocking save bubbles (#75).
18+
- Schema documentation dedupe / `CODE_DERIVED` compatibility initializers (`V116`) (#74, #77).
19+
20+
### Fixed
21+
22+
- Cross-user MCP credential leak in the Agent tab — provisioner no longer last-writer-wins across Hermes profiles; MCP tokens bind to declared client identity (#78).
23+
- View as Agent enforces the target user’s data policy on new Agent threads (#71).
24+
- Chat schema allowlist enumerates the whole statement (#70).
25+
- Brain endpoints require connection content authorization (#72).
26+
- Review queue approvals: stale pending counts, bulk `failures[]`, approval path unwedge (#74, #77).
27+
- Editor CSV export bounded; concurrent-run guards and cancel audit (#76).
28+
29+
### Changed
30+
31+
- Documented CORS loopback wildcards required for Desktop SSH tunnels (`CORS_ALLOWED_ORIGINS`).
32+
- `@deepsql/mcp``0.27.1`.
33+
- DeepSQL Desktop package → `1.0.0` (cut installers with `desktop-v1.0.0`).
34+
35+
### Notes
36+
37+
- Open follow-up: dashboard workspaces + custom roles (#80) intentionally not in this cut.
38+
1139
## [1.2.0] — 2026-08-19
1240

1341
### Added
@@ -82,6 +110,7 @@ First public OSS release.
82110
- Residual high-severity items tracked in `docs/oss-ux/OSS_SECURITY_REVIEW.md` (IDOR sweep, SET preamble allowlist, SSRF hardening, share-password defaults) are deferred past this cut.
83111
- Primary distribution path remains `docker compose up --build` (no pre-built container registry in this release).
84112

113+
[1.3.0]: https://github.com/DeepSQLAI/deepsql/releases/tag/v1.3.0
85114
[1.2.0]: https://github.com/DeepSQLAI/deepsql/releases/tag/v1.2.0
86115
[1.1.0]: https://github.com/DeepSQLAI/deepsql/releases/tag/v1.1.0
87116
[1.0.0]: https://github.com/DeepSQLAI/deepsql/releases/tag/v1.0.0

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ control except the prompts you send to the endpoint you chose.
1515
📄 **[Read the whitepaper](https://deepsql.ai/whitepaper)** — the architecture and the
1616
reasoning behind it.
1717

18+
📦 **Latest release: [v1.3.0](https://github.com/DeepSQLAI/deepsql/releases/tag/v1.3.0)**
19+
DeepSQL Desktop first ship + Agent/Brain/Editor hardening. Notes:
20+
[`docs/releases/RELEASE_NOTES-v1.3.0.md`](docs/releases/RELEASE_NOTES-v1.3.0.md).
21+
1822
---
1923

2024
## Quick start

backend/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<groupId>com.dbaagent</groupId>
1616
<artifactId>dba-agent-backend</artifactId>
17-
<version>1.2.0</version>
17+
<version>1.3.0</version>
1818
<name>DBA Agent Backend</name>
1919
<description>Backend service for DBA Agent with secure credential storage and schema analysis</description>
2020

backend/src/main/java/com/dbaagent/controller/SetupController.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.dbaagent.service.SystemConfigService;
77
import lombok.RequiredArgsConstructor;
88
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.beans.factory.annotation.Value;
910
import org.springframework.http.ResponseEntity;
1011
import org.springframework.web.bind.annotation.*;
1112
import org.springframework.web.client.RestClient;
@@ -34,6 +35,25 @@ public class SetupController {
3435
private final CredentialRepository credentialRepository;
3536
private final LlmConfigResolver llmConfigResolver;
3637

38+
/**
39+
* Mirrors {@code security.google.enabled}. Surfaced on the public status
40+
* endpoint so the login page can decide whether to offer Google sign-in —
41+
* it is otherwise unauthenticated and has no way to know the server was
42+
* configured for SSO. Deliberately NOT final: {@code @RequiredArgsConstructor}
43+
* would pull a final field into the constructor and Spring has no bean to
44+
* satisfy it.
45+
*/
46+
@Value("${security.google.enabled:false}")
47+
private boolean googleEnabled;
48+
49+
/**
50+
* Mirrors {@code security.password.enabled}. Lets the login page hide the
51+
* email/password form on SSO-only installs instead of rendering a form that
52+
* always fails. Same non-final reasoning as above.
53+
*/
54+
@Value("${security.password.enabled:true}")
55+
private boolean passwordLoginEnabled;
56+
3757
// ── GET /setup/status ─────────────────────────────────────────────────────
3858

3959
/** Returns setup completion state. Public endpoint — no auth required. */
@@ -53,7 +73,9 @@ public SetupStatusResponse getStatus() {
5373
setupComplete,
5474
hasOrgInfo,
5575
hasConnections,
56-
hasLlmConfig
76+
hasLlmConfig,
77+
googleEnabled,
78+
passwordLoginEnabled
5779
);
5880
}
5981

@@ -278,7 +300,11 @@ public record SetupStatusResponse(
278300
boolean setupComplete,
279301
boolean hasOrganizationInfo,
280302
boolean hasConnections,
281-
boolean hasLlmConfig
303+
boolean hasLlmConfig,
304+
/** Whether Google Workspace SSO is configured; drives the login page's SSO button. */
305+
boolean googleEnabled,
306+
/** Whether email+password sign-in is accepted; false hides the password form. */
307+
boolean passwordLoginEnabled
282308
) {}
283309

284310
public record InitializeRequest(String orgName, String adminUsername, String adminEmail, String adminPassword) {}

backend/src/main/java/com/dbaagent/service/PasswordlessAuthService.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import com.dbaagent.repository.*;
55
import com.dbaagent.security.EncryptionService;
66
import com.dbaagent.util.SecurityHashUtil;
7+
import jakarta.annotation.PostConstruct;
78
import lombok.Builder;
89
import lombok.RequiredArgsConstructor;
10+
import lombok.extern.slf4j.Slf4j;
911
import org.springframework.beans.factory.annotation.Value;
1012
import org.springframework.http.HttpStatus;
1113
import org.springframework.http.MediaType;
@@ -25,6 +27,7 @@
2527

2628
@Service
2729
@RequiredArgsConstructor
30+
@Slf4j
2831
public class PasswordlessAuthService {
2932
private static final SecureRandom RANDOM = new SecureRandom();
3033

@@ -72,6 +75,32 @@ public class PasswordlessAuthService {
7275
@Value("${security.google.enabled:false}")
7376
private boolean googleEnabled;
7477

78+
/**
79+
* Whether email + password sign-in is accepted at all.
80+
*
81+
* <p>Defaults to {@code true} so existing installs are unaffected. Set it to
82+
* false on deployments that front DeepSQL with Google Workspace SSO: enabling
83+
* SSO does NOT by itself close the password path, so without this flag
84+
* {@code /auth/login} stays open to every local account even when every human
85+
* signs in through Google.
86+
*
87+
* <p>Turning this off while {@code security.google.enabled} is also off leaves
88+
* no way to sign in — {@link #warnIfNoAuthMethodEnabled()} shouts about that at
89+
* startup rather than letting it be discovered at the login screen.
90+
*/
91+
@Value("${security.password.enabled:true}")
92+
private boolean passwordLoginEnabled;
93+
94+
@PostConstruct
95+
void warnIfNoAuthMethodEnabled() {
96+
if (!passwordLoginEnabled && !googleEnabled) {
97+
log.error("security.password.enabled=false AND security.google.enabled=false — "
98+
+ "no sign-in method is available and nobody can log in. Enable one of them.");
99+
} else if (!passwordLoginEnabled) {
100+
log.info("Password sign-in is DISABLED (security.password.enabled=false); Google SSO only.");
101+
}
102+
}
103+
75104
@Value("${security.google.client-id:}")
76105
private String googleClientId;
77106

@@ -87,6 +116,23 @@ public class PasswordlessAuthService {
87116
@Transactional
88117
public AuthFlowResult loginWithPassword(String email, String password, String clientIp, String userAgent, String requestId) {
89118
String normalizedEmail = normalizeEmail(email);
119+
120+
// Checked before the rate limiter and before any credential comparison:
121+
// when the password path is closed there is nothing to rate-limit and no
122+
// secret to compare, and we must not leak whether the account exists.
123+
if (!passwordLoginEnabled) {
124+
securityEventService.log(SecurityEventService.EventRequest.builder()
125+
.eventType(SecurityEventType.PASSWORD_LOGIN_FAILURE)
126+
.outcome(SecurityEventOutcome.FAILURE)
127+
.email(normalizedEmail)
128+
.clientIp(clientIp)
129+
.userAgent(userAgent)
130+
.requestId(requestId)
131+
.metadata(Map.of("reason", "password_login_disabled"))
132+
.build());
133+
return AuthFlowResult.invalid("Password sign-in is disabled. Please sign in with Google.");
134+
}
135+
90136
if (rateLimitEnabled) enforcePasswordRateLimit(normalizedEmail, clientIp);
91137

92138
User user = normalizedEmail == null ? null : userRepository.findByEmailIgnoreCase(normalizedEmail).orElse(null);

backend/src/main/resources/application-prod.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ spring.threads.virtual.enabled=true
77

88
# Security Configuration - set to false to bypass authentication
99
security.auth.enabled=true
10+
security.password.enabled=${SECURITY_PASSWORD_ENABLED:true}
1011
security.jwt.secret=${SECURITY_JWT_SECRET:}
1112
# First-user bootstrap. Off by default: the endpoint creates an ADMIN without
1213
# authenticating, so it stays shut unless someone is deliberately installing.

backend/src/main/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ spring.threads.virtual.enabled=true
1616
# matches production behavior. Override with SECURITY_AUTH_ENABLED=false only
1717
# for explicit single-user bypass scenarios.
1818
security.auth.enabled=${SECURITY_AUTH_ENABLED:true}
19+
# Email+password sign-in. Defaults true so existing installs are unaffected.
20+
# Set false on SSO-only deployments: enabling Google does NOT close /auth/login.
21+
security.password.enabled=${SECURITY_PASSWORD_ENABLED:true}
1922
security.admin-mfa.enabled=${SECURITY_ADMIN_MFA_ENABLED:false}
2023
security.jwt.secret=${SECURITY_JWT_SECRET:}
2124
security.admin.bootstrap.enabled=${SECURITY_ADMIN_BOOTSTRAP_ENABLED:false}

backend/src/test/java/com/dbaagent/service/PasswordlessAuthServiceTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ void setUp() {
5757
ReflectionTestUtils.setField(service, "maxIpStarts", 20);
5858
ReflectionTestUtils.setField(service, "maxPasswordFailures", 10);
5959
ReflectionTestUtils.setField(service, "adminMfaEnabled", false);
60+
// @Value is not processed by @InjectMocks, so a boolean field defaults to
61+
// false. Unlike rateLimitEnabled (checked as `if (enabled)`, so absence
62+
// merely skips it), password login is checked as `if (!enabled)` — leaving
63+
// it unset would reject every login below.
64+
ReflectionTestUtils.setField(service, "passwordLoginEnabled", true);
6065

6166
when(authLoginChallengeRepository.save(any(AuthLoginChallenge.class)))
6267
.thenAnswer(invocation -> invocation.getArgument(0));

desktop/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# DeepSQL Desktop
22

3+
**Version 1.0.0** — first public Desktop cut (ships with DeepSQL product `v1.3.0`).
4+
35
A cross-platform desktop client for a self-hosted DeepSQL server. It connects to
46
the VM (or bare metal) running the DeepSQL stack either **directly over TLS** or
57
through an **SSH tunnel**, and presents the DeepSQL UI in a native window with

desktop/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)