Skip to content

Commit 3843623

Browse files
authored
Merge pull request #266 from dekart-xyz/fix-cloud-regression-for-sso
Enhance authentication flow and add regression tests
2 parents dc412eb + 638156a commit 3843623

7 files changed

Lines changed: 91 additions & 30 deletions

File tree

.github/workflows/e2e.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
sleep $((attempt * 10))
7777
done
7878
79-
- name: E2E part 1
79+
- name: E2E part 1 (start + login regression)
8080
run: |
8181
docker run -i \
8282
--network="host" \
@@ -99,7 +99,7 @@ jobs:
9999
-e DEKART_GOOGLE_OAUTH_SECRET=${{ secrets.GOOGLE_OAUTH_SECRET }} \
100100
-e DEKART_DEV_REFRESH_TOKEN=${{ secrets.DEV_REFRESH_TOKEN_INFO }} \
101101
-e DEKART_DATA_ENCRYPTION_KEY=projects/910252011719/secrets/dekart-dev-user-data-encoding-key/versions/latest \
102-
-e DEKART_CORS_ORIGIN=http://localhost:3000 \
102+
-e DEKART_CORS_ORIGIN=http://localhost:3000 \
103103
-e DEKART_ALLOW_WORKSPACE_CREATION=1 \
104104
-e DEKART_UX_DISABLE_VERSION_CHECK=1 \
105105
-e DEKART_DEV_NO_DATASET_CACHE=1 \

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Self-check before finalizing:
2525

2626
- Follow existing project conventions before introducing new patterns.
2727
- Prefer established libraries for standard concerns over custom implementations.
28+
- Use canonical state, not ad-hoc component logic: before deriving behavior in components, check reducers/actions/selectors for an existing field and reuse it; if missing, add it once in the state layer (reducer/selector) and consume that everywhere.
2829
- Do not use debug-level logging in production code paths.
2930
- Keep test structure consistent with neighboring tests in the same folder.
3031
- Match existing UI patterns and placement
@@ -72,4 +73,3 @@ Why: this environment exports `ELECTRON_RUN_AS_NODE=1` by default, which prevent
7273
2. Re-run with env override:
7374
- `ELECTRON_RUN_AS_NODE= npx cypress run --spec "<spec-path>"`
7475

75-

cypress/e2e/cloud/cloudBasicFlowStart.cy.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ describe('cloud basic flow', () => {
44
it('with info token', () => {
55
// create workspace
66
cy.visit('/')
7-
cy.get('button:contains("Create Workspace")').click()
7+
cy.get('button#dekart-create-workspace').click()
88
cy.get('input#name').type('test')
99
cy.get('button:contains("Create")').click()
1010

@@ -41,4 +41,35 @@ describe('cloud basic flow', () => {
4141
cy.get('button:contains("Connect with Google")').click()
4242
cy.get('button:contains("Continue to Google")').should('be.visible')
4343
})
44+
45+
it('anonymous public report login uses auth redirect', () => {
46+
cy.visit('/')
47+
cy.get('button#dekart-create-report').click()
48+
cy.get('button:contains("Upload File")').click()
49+
cy.get('input[type="file"]').selectFile('cypress/fixtures/sample.csv', { force: true })
50+
cy.get('button:contains("Upload")').click()
51+
cy.get('div:contains("8,276 rows")', { timeout: 20000 }).should('be.visible')
52+
53+
cy.get('button#dekart-share-report').click()
54+
cy.get('button#dekart-publish-report').click()
55+
cy.get('button#dekart-publish-report')
56+
.should('have.class', 'ant-switch-checked')
57+
.and('not.have.class', 'ant-switch-loading')
58+
59+
cy.location('pathname').then((reportPath) => {
60+
cy.clearLocalStorage()
61+
cy.window().then((win) => {
62+
win.sessionStorage.clear()
63+
})
64+
cy.visit(reportPath)
65+
})
66+
67+
cy.get('#dekart-login-button').should('be.visible').and('contain', 'Login with Google')
68+
cy.contains('button', 'Create Map').should('not.exist')
69+
cy.contains('Login requires SSO').should('not.exist')
70+
71+
cy.intercept('GET', '**/api/v1/authenticate*').as('authenticate')
72+
cy.get('#dekart-login-button').click()
73+
cy.wait('@authenticate')
74+
})
4475
})

src/client/Header.jsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ function User ({ buttonDivider }) {
3535
const token = useSelector(state => state.token)
3636
const history = useHistory()
3737
const userStream = useSelector(state => state.user.stream)
38-
const { authEnabled } = useSelector(state => state.env)
38+
const env = useSelector(state => state.env)
39+
const { authEnabled, authConfigured, isSnowpark } = env
3940
const isPlayground = useSelector(state => state.user.isPlayground)
4041
const isDefaultWorkspace = useSelector(state => state.user.isDefaultWorkspace)
41-
const isSnowpark = useSelector(state => state.env.isSnowpark)
4242
const dispatch = useDispatch()
4343
const [showSSOModal, setShowSSOModal] = useState(false)
4444

@@ -68,6 +68,18 @@ function User ({ buttonDivider }) {
6868
disabled: true
6969
}
7070
)
71+
const handleLoginClick = () => {
72+
if (!authConfigured) {
73+
setShowSSOModal(true)
74+
return
75+
}
76+
const state = new AuthState()
77+
state.setUiUrl(window.location.href)
78+
state.setAction(AuthState.Action.ACTION_REQUEST_CODE)
79+
dispatch(authRedirect(state))
80+
}
81+
const loginButtonText = env.isCloud ? 'Login with Google' : 'Login'
82+
7183
return (
7284
<>
7385
<div className={classNames(
@@ -77,7 +89,7 @@ function User ({ buttonDivider }) {
7789
>
7890
{isAnonymous
7991
? (
80-
<Button id='dekart-login-button' type='primary' onClick={() => setShowSSOModal(true)}>Login</Button>
92+
<Button id='dekart-login-button' type='primary' onClick={handleLoginClick}>{loginButtonText}</Button>
8193
)
8294
: (
8395
<Dropdown

src/client/ReportHeaderButtons.jsx

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -194,29 +194,34 @@ function useRequireWorkspace () {
194194

195195
function WorkspaceOnboarding () {
196196
const history = useHistory()
197+
const isAnonymous = useSelector(state => state.user.isAnonymous)
197198
useEffect(() => {
198199
track('WorkspaceOnboarding')
199200
}, [])
200201
return (
201202
<div className={styles.reportHeaderButtons}>
202-
<Button
203-
type='text' icon={<InfoCircleOutlined />}
204-
href='https://dekart.xyz/?ref=about-maps-button'
205-
target='_blank'
206-
title='About Dekart Maps'
207-
onClick={() => {
208-
track('AboutDekartMaps')
209-
}}
210-
>About Dekart Maps
211-
</Button>
212-
<Button
213-
icon={<PlusOutlined />}
214-
type='primary' onClick={() => {
215-
track('WorkspaceOnboardingCreateMap')
216-
history.push('/workspace')
217-
}}
218-
>Create Map
219-
</Button>
203+
<div className={styles.hideOnMobile}>
204+
<Button
205+
type='text' icon={<InfoCircleOutlined />}
206+
href='https://dekart.xyz/?ref=about-maps-button'
207+
target='_blank'
208+
title='About this map'
209+
onClick={() => {
210+
track('AboutDekartMaps')
211+
}}
212+
>About this map
213+
</Button>
214+
</div>
215+
{!isAnonymous && (
216+
<Button
217+
icon={<PlusOutlined />}
218+
type='primary' onClick={() => {
219+
track('WorkspaceOnboardingCreateMap')
220+
history.push('/workspace')
221+
}}
222+
>Create Map
223+
</Button>
224+
)}
220225
</div>
221226
)
222227
}

src/client/WorkspacePage.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ function WelcomeScreen ({ setNextStep }) {
334334
extra={(
335335
<>
336336
<Button
337+
id='dekart-create-workspace'
337338
type='primary' key='1' disabled={invites.length > 0} onClick={() => {
338339
track('CreateWorkspace')
339340
setNextStep('workspace')

src/client/reducers/rootReducer.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,28 +75,40 @@ function usage (state = defaultUsage, action) {
7575
}
7676
}
7777

78-
const defaultEnv = { loaded: false, variables: {}, authEnabled: null, authType: 'UNSPECIFIED', serverTime: null, receivedTime: null }
78+
const defaultEnv = { loaded: false, variables: {}, authEnabled: null, authConfigured: false, authType: 'UNSPECIFIED', serverTime: null, receivedTime: null }
7979
function env (state = defaultEnv, action) {
8080
switch (action.type) {
8181
case setRedirectState.name:
8282
return {
8383
...state,
8484
loaded: false // reset when user auth details like token change
8585
}
86-
case setEnv.name:
86+
case setEnv.name: {
87+
const googleOAuthEnabled = action.variables.REQUIRE_GOOGLE_OAUTH === '1'
88+
const oidcEnabled = action.variables.REQUIRE_OIDC === '1'
89+
const isSnowpark = action.variables.IS_SNOWPARK === '1'
90+
const authConfigured = (
91+
googleOAuthEnabled ||
92+
oidcEnabled ||
93+
action.variables.REQUIRE_IAP === '1' ||
94+
action.variables.REQUIRE_AMAZON_OIDC === '1' ||
95+
isSnowpark
96+
)
8797
return {
8898
loaded: true,
8999
variables: action.variables,
90100
authEnabled: action.variables.AUTH_ENABLED === '1',
91-
googleOAuthEnabled: action.variables.REQUIRE_GOOGLE_OAUTH === '1',
92-
oidcEnabled: action.variables.REQUIRE_OIDC === '1',
101+
authConfigured,
102+
googleOAuthEnabled,
103+
oidcEnabled,
93104
secretsEnabled: action.variables.SECRETS_ENABLED === '1',
94105
uxConfig: JSON.parse(action.variables.CLOUD_UX_CONFIG_JSON || '{}'),
95106
isCloud: action.variables.DEKART_CLOUD === '1',
96-
isSnowpark: action.variables.IS_SNOWPARK === '1',
107+
isSnowpark,
97108
serverTime: action.serverTime,
98109
receivedTime: Math.floor(Date.now() / 1000)
99110
}
111+
}
100112
default:
101113
return state
102114
}

0 commit comments

Comments
 (0)