Skip to content

Commit e5af338

Browse files
susnuxAndyScherzinger
authored andcommitted
test: make cypress run in secure context and add WebAuthn tests
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 3514ac0 commit e5af338

File tree

9 files changed

+315
-120
lines changed

9 files changed

+315
-120
lines changed

.eslintrc.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ module.exports = {
3535
jsdoc: {
3636
mode: 'typescript',
3737
},
38+
'import/resolver': {
39+
typescript: {}, // this loads <rootdir>/tsconfig.json to eslint
40+
},
3841
},
3942
overrides: [
4043
// Allow any in tests
@@ -43,6 +46,6 @@ module.exports = {
4346
rules: {
4447
'@typescript-eslint/no-explicit-any': 'warn',
4548
},
46-
}
49+
},
4750
],
4851
}

cypress/dockerNode.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ export const startNextcloud = async function(branch: string = getCurrentGitBranc
8888
Type: 'tmpfs',
8989
ReadOnly: false,
9090
}],
91+
PortBindings: {
92+
'80/tcp': [{
93+
HostIP: '0.0.0.0',
94+
HostPort: '8083',
95+
}],
96+
},
9197
},
9298
Env: [
9399
`BRANCH=${branch}`,
@@ -242,11 +248,15 @@ export const getContainerIP = async function(
242248
while (ip === '' && tries < 10) {
243249
tries++
244250

245-
await container.inspect(function(err, data) {
251+
container.inspect(function(err, data) {
246252
if (err) {
247253
throw err
248254
}
249-
ip = data?.NetworkSettings?.IPAddress || ''
255+
if (data?.HostConfig.PortBindings?.['80/tcp']?.[0]?.HostPort) {
256+
ip = `localhost:${data.HostConfig.PortBindings['80/tcp'][0].HostPort}`
257+
} else {
258+
ip = data?.NetworkSettings?.IPAddress || ''
259+
}
250260
})
251261

252262
if (ip !== '') {

cypress/e2e/files/LivePhotosUtils.ts

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,25 @@ type SetupInfo = {
1414
}
1515

1616
/**
17-
*
18-
* @param user
19-
* @param fileName
20-
* @param domain
21-
* @param requesttoken
22-
* @param metadata
2317
*/
2418
function setMetadata(user: User, fileName: string, requesttoken: string, metadata: object) {
25-
cy.url().then(url => {
26-
const hostname = new URL(url).hostname
27-
cy.request({
28-
method: 'PROPPATCH',
29-
url: `http://${hostname}/remote.php/dav/files/${user.userId}/${fileName}`,
30-
auth: { user: user.userId, pass: user.password },
31-
headers: {
32-
requesttoken,
33-
},
34-
body: `<?xml version="1.0"?>
35-
<d:propertyupdate xmlns:d="DAV:" xmlns:nc="http://nextcloud.org/ns">
36-
<d:set>
37-
<d:prop>
38-
${Object.entries(metadata).map(([key, value]) => `<${key}>${value}</${key}>`).join('\n')}
39-
</d:prop>
40-
</d:set>
41-
</d:propertyupdate>`,
42-
})
19+
const base = Cypress.config('baseUrl')!.replace(/\/index\.php\/?/, '')
20+
cy.request({
21+
method: 'PROPPATCH',
22+
url: `${base}/remote.php/dav/files/${user.userId}/${fileName}`,
23+
auth: { user: user.userId, pass: user.password },
24+
headers: {
25+
requesttoken,
26+
},
27+
body: `<?xml version="1.0"?>
28+
<d:propertyupdate xmlns:d="DAV:" xmlns:nc="http://nextcloud.org/ns">
29+
<d:set>
30+
<d:prop>
31+
${Object.entries(metadata).map(([key, value]) => `<${key}>${value}</${key}>`).join('\n')}
32+
</d:prop>
33+
</d:set>
34+
</d:propertyupdate>`,
4335
})
44-
4536
}
4637

4738
/**

cypress/e2e/files_external/files-user-credentials.cy.ts

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@ describe('Files user credentials', { testIsolation: true }, () => {
1515
let user2: User
1616
let storageUser: User
1717

18-
beforeEach(() => {
19-
})
20-
2118
before(() => {
2219
cy.runOccCommand('app:enable files_external')
2320

2421
// Create some users
25-
cy.createRandomUser().then((user) => user1 = user)
26-
cy.createRandomUser().then((user) => user2 = user)
22+
cy.createRandomUser().then((user) => {
23+
user1 = user
24+
})
25+
cy.createRandomUser().then((user) => {
26+
user2 = user
27+
})
2728

2829
// This user will hold the webdav storage
2930
cy.createRandomUser().then((user) => {
@@ -34,7 +35,7 @@ describe('Files user credentials', { testIsolation: true }, () => {
3435

3536
after(() => {
3637
// Cleanup global storages
37-
cy.runOccCommand(`files_external:list --output=json`).then(({stdout}) => {
38+
cy.runOccCommand('files_external:list --output=json').then(({ stdout }) => {
3839
const list = JSON.parse(stdout)
3940
list.forEach((storage) => cy.runOccCommand(`files_external:delete --yes ${storage.mount_id}`), { failOnNonZeroExit: false })
4041
})
@@ -43,8 +44,10 @@ describe('Files user credentials', { testIsolation: true }, () => {
4344
})
4445

4546
it('Create a user storage with user credentials', () => {
46-
const url = Cypress.config('baseUrl') + '/remote.php/dav/files/' + storageUser.userId
47-
createStorageWithConfig(storageUser.userId, StorageBackend.DAV, AuthBackend.UserProvided, { host: url.replace('index.php/', ''), 'secure': 'false' })
47+
// Its not the public server address but the address so the server itself can connect to it
48+
const base = 'http://localhost'
49+
const host = `${base}/remote.php/dav/files/${storageUser.userId}`
50+
createStorageWithConfig(storageUser.userId, StorageBackend.DAV, AuthBackend.UserProvided, { host, secure: 'false' })
4851

4952
cy.login(user1)
5053
cy.visit('/apps/files/extstoragemounts')
@@ -71,7 +74,8 @@ describe('Files user credentials', { testIsolation: true }, () => {
7174
cy.wait('@setCredentials')
7275

7376
// Auth dialog should be closed and the set credentials button should be gone
74-
authDialog.should('not.exist', { timeout: 2000 })
77+
cy.get('@authDialog').should('not.exist', { timeout: 2000 })
78+
7579
getActionEntryForFile(storageUser.userId, ACTION_CREDENTIALS_EXTERNAL_STORAGE).should('not.exist')
7680

7781
// Finally, the storage should be accessible
@@ -81,8 +85,10 @@ describe('Files user credentials', { testIsolation: true }, () => {
8185
})
8286

8387
it('Create a user storage with GLOBAL user credentials', () => {
84-
const url = Cypress.config('baseUrl') + '/remote.php/dav/files/' + storageUser.userId
85-
createStorageWithConfig('storage1', StorageBackend.DAV, AuthBackend.UserGlobalAuth, { host: url.replace('index.php/', ''), 'secure': 'false' })
88+
// Its not the public server address but the address so the server itself can connect to it
89+
const base = 'http://localhost'
90+
const host = `${base}/remote.php/dav/files/${storageUser.userId}`
91+
createStorageWithConfig('storage1', StorageBackend.DAV, AuthBackend.UserGlobalAuth, { host, secure: 'false' })
8692

8793
cy.login(user2)
8894
cy.visit('/apps/files/extstoragemounts')
@@ -93,23 +99,32 @@ describe('Files user credentials', { testIsolation: true }, () => {
9399
triggerInlineActionForFile('storage1', ACTION_CREDENTIALS_EXTERNAL_STORAGE)
94100

95101
// See credentials dialog
96-
const storageDialog = cy.findByRole('dialog', { name: 'Storage credentials' })
97-
storageDialog.should('be.visible')
98-
storageDialog.findByRole('textbox', { name: 'Login' }).type(storageUser.userId)
99-
storageDialog.get('input[type="password"]').type(storageUser.password)
100-
storageDialog.get('button').contains('Confirm').click()
101-
storageDialog.should('not.exist')
102+
cy.findByRole('dialog', { name: 'Storage credentials' })
103+
.as('storageDialog')
104+
cy.get('@storageDialog')
105+
.should('be.visible')
106+
.findByRole('textbox', { name: 'Login' })
107+
.type(storageUser.userId)
108+
cy.get('@storageDialog')
109+
.find('input[type="password"]')
110+
.type(storageUser.password)
111+
cy.get('@storageDialog')
112+
.contains('button', 'Confirm')
113+
.click()
114+
cy.get('@storageDialog')
115+
.should('not.exist')
102116

103117
// Storage dialog now closed, the user auth dialog should be visible
104-
const authDialog = cy.findByRole('dialog', { name: 'Confirm your password' })
105-
authDialog.should('be.visible')
118+
cy.findByRole('dialog', { name: 'Confirm your password' })
119+
.as('authDialog')
120+
.should('be.visible')
106121
handlePasswordConfirmation(user2.password)
107122

108123
// Wait for the credentials to be set
109124
cy.wait('@setCredentials')
110125

111126
// Auth dialog should be closed and the set credentials button should be gone
112-
authDialog.should('not.exist', { timeout: 2000 })
127+
cy.get('@authDialog').should('not.exist', { timeout: 2000 })
113128
getActionEntryForFile('storage1', ACTION_CREDENTIALS_EXTERNAL_STORAGE).should('not.exist')
114129

115130
// Finally, the storage should be accessible
@@ -119,8 +134,10 @@ describe('Files user credentials', { testIsolation: true }, () => {
119134
})
120135

121136
it('Create another user storage while reusing GLOBAL user credentials', () => {
122-
const url = Cypress.config('baseUrl') + '/remote.php/dav/files/' + storageUser.userId
123-
createStorageWithConfig('storage2', StorageBackend.DAV, AuthBackend.UserGlobalAuth, { host: url.replace('index.php/', ''), 'secure': 'false' })
137+
// Its not the public server address but the address so the server itself can connect to it
138+
const base = 'http://localhost'
139+
const host = `${base}/remote.php/dav/files/${storageUser.userId}`
140+
createStorageWithConfig('storage2', StorageBackend.DAV, AuthBackend.UserGlobalAuth, { host, secure: 'false' })
124141

125142
cy.login(user2)
126143
cy.visit('/apps/files/extstoragemounts')

cypress/e2e/files_versions/version_deletion.cy.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ describe('Versions restoration', () => {
5959
})
6060

6161
it('Does not work without delete permission through direct API access', () => {
62-
let hostname: string
6362
let fileId: string|undefined
6463
let versionId: string|undefined
6564

@@ -68,24 +67,30 @@ describe('Versions restoration', () => {
6867
navigateToFolder(folderName)
6968
openVersionsPanel(randomFilePath)
7069

71-
cy.url().then(url => { hostname = new URL(url).hostname })
72-
getRowForFile(randomFileName).invoke('attr', 'data-cy-files-list-row-fileid').then(_fileId => { fileId = _fileId })
73-
cy.get('[data-files-versions-version]').eq(1).invoke('attr', 'data-files-versions-version').then(_versionId => { versionId = _versionId })
70+
getRowForFile(randomFileName)
71+
.should('be.visible')
72+
.invoke('attr', 'data-cy-files-list-row-fileid')
73+
.then(($fileId) => { fileId = $fileId })
7474

75+
cy.get('[data-files-versions-version]')
76+
.eq(1)
77+
.invoke('attr', 'data-files-versions-version')
78+
.then(($versionId) => { versionId = $versionId })
79+
80+
cy.logout()
7581
cy.then(() => {
76-
cy.logout()
77-
cy.request({
82+
const base = Cypress.config('baseUrl')!.replace(/\/index\.php\/?$/, '')
83+
return cy.request({
7884
method: 'DELETE',
85+
url: `${base}/remote.php/dav/versions/${recipient.userId}/versions/${fileId}/${versionId}`,
7986
auth: { user: recipient.userId, pass: recipient.password },
8087
headers: {
8188
cookie: '',
8289
},
83-
url: `http://${hostname}/remote.php/dav/versions/${recipient.userId}/versions/${fileId}/${versionId}`,
8490
failOnStatusCode: false,
8591
})
86-
.then(({ status }) => {
87-
expect(status).to.equal(403)
88-
})
92+
}).then(({ status }) => {
93+
expect(status).to.equal(403)
8994
})
9095
})
9196
})

cypress/e2e/files_versions/version_download.cy.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,31 +52,36 @@ describe('Versions download', () => {
5252
})
5353

5454
it('Does not work without download permission through direct API access', () => {
55-
let hostname: string
5655
let fileId: string|undefined
5756
let versionId: string|undefined
5857

5958
setupTestSharedFileFromUser(user, randomFileName, { download: false })
60-
.then(recipient => {
59+
.then((recipient) => {
6160
openVersionsPanel(randomFileName)
6261

63-
cy.url().then(url => { hostname = new URL(url).hostname })
64-
getRowForFile(randomFileName).invoke('attr', 'data-cy-files-list-row-fileid').then(_fileId => { fileId = _fileId })
65-
cy.get('[data-files-versions-version]').eq(1).invoke('attr', 'data-files-versions-version').then(_versionId => { versionId = _versionId })
62+
getRowForFile(randomFileName)
63+
.should('be.visible')
64+
.invoke('attr', 'data-cy-files-list-row-fileid')
65+
.then(($fileId) => { fileId = $fileId })
6666

67+
cy.get('[data-files-versions-version]')
68+
.eq(1)
69+
.invoke('attr', 'data-files-versions-version')
70+
.then(($versionId) => { versionId = $versionId })
71+
72+
cy.logout()
6773
cy.then(() => {
68-
cy.logout()
69-
cy.request({
74+
const base = Cypress.config('baseUrl')!.replace(/\/index\.php\/?$/, '')
75+
return cy.request({
76+
url: `${base}/remote.php/dav/versions/${recipient.userId}/versions/${fileId}/${versionId}`,
7077
auth: { user: recipient.userId, pass: recipient.password },
7178
headers: {
7279
cookie: '',
7380
},
74-
url: `http://${hostname}/remote.php/dav/versions/${recipient.userId}/versions/${fileId}/${versionId}`,
7581
failOnStatusCode: false,
7682
})
77-
.then(({ status }) => {
78-
expect(status).to.equal(403)
79-
})
83+
}).then(({ status }) => {
84+
expect(status).to.equal(403)
8085
})
8186
})
8287
})

0 commit comments

Comments
 (0)