diff --git a/Tests/lib/WebDAV.js b/Tests/lib/WebDAV.js index e60831346..0df685c18 100644 --- a/Tests/lib/WebDAV.js +++ b/Tests/lib/WebDAV.js @@ -1,3 +1,4 @@ +import cookie from 'cookie' import { DAVNamespace, DAVNamespaceShorthandMap, @@ -42,7 +43,10 @@ export { class WebDAV { constructor(un, pw) { this.serverUrl = `http://${config.hostname}:${config.port}` + this.cookie = null if (un && pw) { + this.username = un + this.password = pw this.headers = getBasicAuthHeaders({ username: un, password: pw @@ -53,6 +57,54 @@ class WebDAV { } } + // Generic operations + + async getAuthCookie() { + if (!this.cookie) { + const resource = `/SOGo/connect` + const response = await fetch(this.serverUrl + resource, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({userName: this.username, password: this.password}) + }) + const values = response.headers.get('set-cookie').split(/, /) + let authCookies = [] + for (let v of values) { + let c = cookie.parse(v) + for (let authCookie of ['0xHIGHFLYxSOGo', 'XSRF-TOKEN']) { + if (Object.keys(c).includes('0xHIGHFLYxSOGo')) { + authCookies.push(cookie.serialize(authCookie, c[authCookie])) + } + } + } + this.cookie = authCookies.join('; ') + } + return this.cookie + } + + async getHttp(resource) { + const authCookie = await this.getAuthCookie() + const localHeaders = { Cookie: authCookie } + + return await fetch(this.serverUrl + resource, { + method: 'GET', + headers: localHeaders + }) + } + + async postHttp(resource, contentType = 'application/json', data = '') { + const authCookie = await this.getAuthCookie() + const localHeaders = { 'Content-Type': contentType, Cookie: authCookie } + + return await fetch(this.serverUrl + resource, { + method: 'POST', + body: data, + headers: localHeaders + }) + } + + // WebDAV operations + deleteObject(resource) { return deleteObject({ url: this.serverUrl + resource, @@ -60,37 +112,6 @@ class WebDAV { }) } - makeCalendar(resource) { - return makeCalendar({ - url: this.serverUrl + resource, - headers: this.headers - }) - } - - createCalendarObject(resource, filename, calendar) { - return createCalendarObject({ - headers: this.headers, - calendar: { url: this.serverUrl + resource }, // DAVCalendar - filename: filename, - iCalString: calendar - }) - } - - postCaldav(resource, vcalendar, originator, recipients) { - let localHeaders = { 'content-type': 'text/calendar; charset=utf-8'} - - if (originator) - localHeaders.originator = originator - if (recipients && recipients.length > 0) - localHeaders.recipients = recipients.join(',') - - return fetch(this.serverUrl + resource, { - method: 'POST', - body: vcalendar, - headers: { ...this.headers, ...localHeaders } - }) - } - getObject(resource, filename) { let url if (resource.match(/^http/)) @@ -110,6 +131,13 @@ class WebDAV { }) } + makeCollection(resource) { + return makeCollection({ + url: this.serverUrl + resource, + headers: this.headers + }); + } + propfindWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) { const nsShort = DAVnsShortMap[namespace] || DAVInverseShort const formattedProperties = properties.map(p => { @@ -132,7 +160,13 @@ class WebDAV { body: { propfind: { _attributes: { - ...getDAVAttribute([DAVNamespace.DAV]), + ...getDAVAttribute([ + DAVNamespace.CALDAV, + DAVNamespace.CALDAV_APPLE, + DAVNamespace.CALENDAR_SERVER, + DAVNamespace.CARDDAV, + DAVNamespace.DAV + ]), [`xmlns:${nsShort}`]: namespace }, prop: formattedProperties @@ -179,6 +213,151 @@ class WebDAV { }) } + propfindURL(resource = '/SOGo/dav') { + return propfind({ + url: this.serverUrl + resource, + depth: '1', + props: [ + { name: 'displayname', namespace: DAVNamespace.DAV }, + { name: 'resourcetype', namespace: DAVNamespace.DAV } + ], + headers: this.headers + }) + } + + propfindCollection(resource) { + return propfind({ + url: this.serverUrl + resource, + headers: this.headers + }) + } + + // http://tools.ietf.org/html/rfc3253.html#section-3.8 + expendProperty(resource, properties) { + return davRequest({ + url: this.serverUrl + resource, + init: { + method: 'REPORT', + namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV], + headers: this.headers, + body: { + 'expand-property': { + _attributes: getDAVAttribute([ + DAVNamespace.DAV, + ]), + [`${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:property`]: properties + } + } + }, + }) + } + + proppatchWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) { + const nsShort = DAVNamespaceShorthandMap[namespace] || DAVInverseShort + const formattedProperties = Object.keys(properties).map(p => { + if (Array.isArray(properties[p])) { + return { [`${nsShort}:${p}`]: properties[p].map(pp => { + const [ key ] = Object.keys(pp) + return { [`${nsShort}:${key}`]: pp[key] || '' } + })} + } + return { [`${nsShort}:${p}`]: properties[p] || '' } + }) + if (typeof headers.depth == 'undefined') { + headers.depth = new String(0) + } + return davRequest({ + url: this.serverUrl + resource, + init: { + method: 'PROPPATCH', + headers: { ...this.headers, ...headers }, + namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV], + body: { + propertyupdate: { + _attributes: { + ...getDAVAttribute([ + DAVNamespace.CALDAV, + DAVNamespace.CALDAV_APPLE, + DAVNamespace.CALENDAR_SERVER, + DAVNamespace.CARDDAV, + DAVNamespace.DAV + ]), + [`xmlns:${nsShort}`]: namespace + }, + set: { + prop: formattedProperties + } + } + } + } + }) + } + + currentUserPrivilegeSet(resource) { + return propfind({ + url: this.serverUrl + resource, + depth: '0', + props: [ + { name: 'current-user-privilege-set', namespace: DAVNamespace.DAV } + ], + headers: this.headers + }) + } + + options(resource) { + return davRequest({ + url: this.serverUrl + resource, + init: { + method: 'OPTIONS', + headers: this.headers, + body: null + }, + convertIncoming: false + }) + } + + principalCollectionSet(resource = '/SOGo/dav') { + return propfind({ + url: this.serverUrl + resource, + depth: '0', + props: [{ name: 'principal-collection-set', namespace: DAVNamespace.DAV }], + headers: this.headers + }) + } + + // CalDAV operations + + makeCalendar(resource) { + return makeCalendar({ + url: this.serverUrl + resource, + headers: this.headers + }) + } + + createCalendarObject(resource, filename, calendar) { + return createCalendarObject({ + headers: this.headers, + calendar: { url: this.serverUrl + resource }, // DAVCalendar + filename: filename, + iCalString: calendar + }) + } + + postCaldav(resource, vcalendar, originator, recipients) { + let localHeaders = { 'content-type': 'text/calendar; charset=utf-8'} + + if (originator) + localHeaders.originator = originator + if (recipients && recipients.length > 0) + localHeaders.recipients = recipients.join(',') + + return fetch(this.serverUrl + resource, { + method: 'POST', + body: vcalendar, + headers: { ...this.headers, ...localHeaders } + }) + } + propfindEvent(resource) { return propfind({ url: this.serverUrl + resource, @@ -201,34 +380,6 @@ class WebDAV { }) } - principalCollectionSet(resource = '/SOGo/dav') { - return propfind({ - url: this.serverUrl + resource, - depth: '0', - props: [{ name: 'principal-collection-set', namespace: DAVNamespace.DAV }], - headers: this.headers - }) - } - - propfindURL(resource = '/SOGo/dav') { - return propfind({ - url: this.serverUrl + resource, - depth: '1', - props: [ - { name: 'displayname', namespace: DAVNamespace.DAV }, - { name: 'resourcetype', namespace: DAVNamespace.DAV } - ], - headers: this.headers - }) - } - - propfindCollection(resource) { - return propfind({ - url: this.serverUrl + resource, - headers: this.headers - }) - } - principalPropertySearch(resource) { return davRequest({ url: `${this.serverUrl}/SOGo/dav`, @@ -255,26 +406,6 @@ class WebDAV { }) } - // http://tools.ietf.org/html/rfc3253.html#section-3.8 - expendProperty(resource, properties) { - return davRequest({ - url: this.serverUrl + resource, - init: { - method: 'REPORT', - namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV], - headers: this.headers, - body: { - 'expand-property': { - _attributes: getDAVAttribute([ - DAVNamespace.DAV, - ]), - [`${DAVNamespaceShorthandMap[DAVNamespace.DAV]}:property`]: properties - } - } - }, - }) - } - syncColletion(resource) { return davRequest({ url: this.serverUrl + resource, @@ -330,112 +461,15 @@ class WebDAV { }) } - propfindCaldav(resource, properties, depth = 0, parseOutgoing = true) { - const formattedProperties = properties.map(p => { return { name: p, namespace: DAVNamespace.CALDAV } }) - return davRequest({ - url: this.serverUrl + resource, - init: { - method: 'PROPFIND', - headers: { ...this.headers, depth: new String(depth) }, - namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV], - body: { - propfind: { - _attributes: getDAVAttribute([ - DAVNamespace.CALDAV, - DAVNamespace.CALDAV_APPLE, - DAVNamespace.CALENDAR_SERVER, - DAVNamespace.CARDDAV, - DAVNamespace.DAV - ]), - prop: formattedProperties.length ? formatProps(formattedProperties) : null, - } - } - }, - parseOutgoing - }) + propfindCaldav(resource, properties, depth = 0) { + return this.propfindWebdav(resource, properties, DAVNamespace.CALDAV, { depth: new String(depth) }) } proppatchCaldav(resource, properties, headers = {}) { - const formattedProperties = Object.keys(properties).map(p => { - return { name: p, namespace: DAVNamespace.CALDAV, value: properties[p] } - }) - return davRequest({ - url: this.serverUrl + resource, - init: { - method: 'PROPPATCH', - headers: { ...this.headers, ...headers }, - namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV], - body: { - propertyupdate: { - _attributes: getDAVAttribute([ - DAVNamespace.CALDAV, - DAVNamespace.CALDAV_APPLE, - DAVNamespace.CALENDAR_SERVER, - DAVNamespace.CARDDAV, - DAVNamespace.DAV - ]), - set: { - prop: formatProps(formattedProperties) - } - } - } - } - // parseOutgoing - }) + return this.proppatchWebdav(resource, properties, DAVNamespace.CALDAV, headers) } - proppatchWebdav(resource, properties, namespace = DAVNamespace.DAV, headers = {}) { - const nsShort = DAVNamespaceShorthandMap[namespace] || DAVInverseShort - const formattedProperties = Object.keys(properties).map(p => { - if (Array.isArray(properties[p])) { - return { [`${nsShort}:${p}`]: properties[p].map(pp => { - const [ key ] = Object.keys(pp) - return { [`${nsShort}:${key}`]: pp[key] || '' } - })} - } - return { [`${nsShort}:${p}`]: properties[p] || '' } - }) - if (typeof headers.depth == 'undefined') { - headers.depth = new String(0) - } - return davRequest({ - url: this.serverUrl + resource, - init: { - method: 'PROPPATCH', - headers: { ...this.headers, ...headers }, - namespace: DAVNamespaceShorthandMap[DAVNamespace.DAV], - body: { - propertyupdate: { - _attributes: { - ...getDAVAttribute([DAVNamespace.DAV]), - [`xmlns:${nsShort}`]: namespace - }, - set: { - prop: formattedProperties - } - } - } - } - }) - } - - currentUserPrivilegeSet(resource) { - return propfind({ - url: this.serverUrl + resource, - depth: '0', - props: [ - { name: 'current-user-privilege-set', namespace: DAVNamespace.DAV } - ], - headers: this.headers - }) - } - - makeCollection(resource) { - return makeCollection({ - url: this.serverUrl + resource, - headers: this.headers - }); - } + // CardDAV operations getCard(resource, filename) { return davRequest({ @@ -458,17 +492,7 @@ class WebDAV { }) } - options(resource) { - return davRequest({ - url: this.serverUrl + resource, - init: { - method: 'OPTIONS', - headers: this.headers, - body: null - }, - convertIncoming: false - }) - } + // MailDAV operations mailQueryMaildav(resource, properties, filters = {}, sort, ascending = true) { let formattedFilters = {} diff --git a/Tests/spec/HTTPCalendarSpec.js b/Tests/spec/HTTPCalendarSpec.js new file mode 100644 index 000000000..53046c4a7 --- /dev/null +++ b/Tests/spec/HTTPCalendarSpec.js @@ -0,0 +1,50 @@ +import config from '../lib/config' +import WebDAV from '../lib/WebDAV' + +let webdav, resource + +describe('HTTP Calendar', function() { + + beforeAll(async function() { + webdav = new WebDAV(config.username, config.password) + resource = `/SOGo/so/${config.username}/Calendar` + }) + + it('Add Web Calendar', async function() { + const data = { url: config.webCalendarURL } + let url, response, body + + url = `${resource}/addWebCalendar` + response = await webdav.postHttp(url, 'application/json', JSON.stringify(data)) + expect(response.status) + .withContext(`HTTP status code when subscribing to a Web calendar`) + .toBe(200) + + body = await response.json() + expect(Object.keys(body)) + .withContext(`JSON payload when subscribing to a Web calendar`) + .toContain('id') + + const calID = body.id + url = `${resource}/${calID}/reload` + response = await webdav.getHttp(url) + expect(response.status) + .withContext(`HTTP status code when reloading a Web calendar`) + .toBe(200) + + expect(response.headers.get('content-type')) + .withContext(`Content type of response when reloading a Web calendar`) + .toBe('application/json') + body = await response.json() + expect(Object.keys(body)) + .withContext(`JSON payload when reloading a Web calendar`) + .toContain('imported') + + url = `${resource}/${calID}/delete` + response = await webdav.postHttp(url, 'application/json') + expect(response.status) + .withContext(`HTTP status code when unsubscribing to a Web calendar`) + .toBe(204) + }) + +}) diff --git a/Tests/spec/WebDavSyncSpec.js b/Tests/spec/WebDavSyncSpec.js index ea3b28aa1..d181c5937 100644 --- a/Tests/spec/WebDavSyncSpec.js +++ b/Tests/spec/WebDavSyncSpec.js @@ -8,9 +8,6 @@ describe('webdav sync', function() { const webdav_su = new WebDAV(config.superuser, config.superuser_password) const resource = `/SOGo/dav/${config.username}/Calendar/test-webdavsync/` - beforeEach(async function() { - }) - afterEach(async function() { await webdav_su.deleteObject(resource) })