From 2414d226f7480d7ce409bc463e3c3aa9802b4ebf Mon Sep 17 00:00:00 2001 From: AleksanderSklorz <115619721+AleksanderSklorz@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:12:50 +0100 Subject: [PATCH] [ACS-4724] remove minimatch dependency (#1536) * ACS-4724 Removed minimatch dependency * ACS-4724 Updated package-lock json file --- README.md | 8 +-- docs/licences/license-info-5.1.0.md | 1 - package-lock.json | 27 +--------- package.json | 2 - src/authentication/oauth2Auth.ts | 6 +-- src/utils/path-matcher.ts | 12 +++++ test/oauth2Auth.spec.ts | 46 ++++++++-------- test/path-matcher.spec.ts | 84 +++++++++++++++++++++++++++++ 8 files changed, 128 insertions(+), 58 deletions(-) create mode 100644 src/utils/path-matcher.ts create mode 100644 test/path-matcher.spec.ts diff --git a/README.md b/README.md index b85713bd05..5e9665eb5d 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ With this authentication the ticket is not validated against the server // Login with ECM ticket const alfrescoApi = new AlfrescoApi({ - ticketEcm:'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1', + ticketEcm:'TICKET_4479f4d3bb155195879bfbb8d5206f433488a1b1', hostEcm:'http://127.0.0.1:8080' }); @@ -247,7 +247,7 @@ redirectLogout| url to be redirect after logout optional, if is nor present the refreshTokenTimeout| millisecond value, after how many millisecond you want refresh the token| 30000| redirectSilentIframeUri| url to be redirect after silent refresh login| /assets/silent-refresh.html | silentLogin| direct execute the implicit login without the need to call AlfrescoJsApi.implicitLogin() method| false| -publicUrls | list of public urls that don't need authorization. It is possible too pass absolute paths and string patterns that are valid for [minimatch](https://github.com/isaacs/minimatch#readme) | +publicUrls | list of public urls that don't need authorization. It is possible too pass absolute paths and string patterns. In patterns you can use * or ** wildcards. Single means that you can have anything in one part of URL for example http://some-public-url/path/* matches with http://some-public-url/path/test. Double means that you can have anything in any number of parts, for example http://some-public-url/path/** matches with http://some-public-url/path/test/some-test.| authorizationUrl| authorization url, relative to the host| /protocol/openid-connect/auth| tokenUrl| token url, relative to the host| /protocol/openid-connect/token| logoutUrl| logout url, relative to the host| /protocol/openid-connect/logout| @@ -358,7 +358,7 @@ logout() alfrescoJsApi.logout().then( data => { console.log('Successfully Logout'); - }, + }, error => { console.error('Possible ticket already expired'); } @@ -527,7 +527,7 @@ alfrescoJsApi.nodes .then( data => { console.log('This is the name' + data.name ); - }, + }, error => { console.log('This node does not exist'); } diff --git a/docs/licences/license-info-5.1.0.md b/docs/licences/license-info-5.1.0.md index f9ec022cc5..93f6cc5a38 100644 --- a/docs/licences/license-info-5.1.0.md +++ b/docs/licences/license-info-5.1.0.md @@ -38,7 +38,6 @@ This page lists all third party libraries the project depends on. | [mime-db](https://github.com/jshttp/mime-db) | 1.40.0 | [MIT](http://www.opensource.org/licenses/MIT) | | [mime-types](https://github.com/jshttp/mime-types) | 2.1.24 | [MIT](http://www.opensource.org/licenses/MIT) | | [mime](https://github.com/broofa/mime) | 2.6.0 | [MIT](http://www.opensource.org/licenses/MIT) | -| [minimatch](https://github.com/isaacs/minimatch) | 5.0.1 | [ISC](https://www.isc.org/downloads/software-support-policy/isc-license/) | | [ms](https://github.com/zeit/ms) | 2.1.2 | [MIT](http://www.opensource.org/licenses/MIT) | | [next-tick](https://github.com/medikoo/next-tick) | 1.0.0 | [MIT](http://www.opensource.org/licenses/MIT) | | [object-inspect](https://github.com/inspect-js/object-inspect) | 1.12.0 | [MIT](http://www.opensource.org/licenses/MIT) | diff --git a/package-lock.json b/package-lock.json index 6c929b8de6..ef2520c3d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -986,12 +986,6 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, - "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, "@types/mocha": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", @@ -1544,7 +1538,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -4338,24 +4333,6 @@ "mime-db": "1.40.0" } }, - "minimatch": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.2.0.tgz", - "integrity": "sha512-rMRHmwySzopAQjmWW6TkAKCEDKNaY/HuV/c2YkWWuWnfkTwApt0V4hnYzzPnZ/5Gcd2+8MPncSyuOGPl3xPvcg==", - "requires": { - "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - } - } - }, "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", diff --git a/package.json b/package.json index 52e2c589aa..e0dfde5a7b 100644 --- a/package.json +++ b/package.json @@ -33,14 +33,12 @@ }, "dependencies": { "event-emitter": "^0.3.5", - "minimatch": "7.2.0", "superagent": "^6.0.0", "tslib": "^2.0.0" }, "devDependencies": { "@types/chai": "^4.2.3", "@types/event-emitter": "^0.3.3", - "@types/minimatch": "^3.0.3", "@types/mocha": "^10.0.1", "@types/node": "^18.13.0", "@types/sinon": "^10.0.1", diff --git a/src/authentication/oauth2Auth.ts b/src/authentication/oauth2Auth.ts index 563c0a04f2..465d94014c 100644 --- a/src/authentication/oauth2Auth.ts +++ b/src/authentication/oauth2Auth.ts @@ -23,11 +23,9 @@ import { AuthenticationApi } from '../api/auth-rest-api/api/authentication.api'; import { AlfrescoApi } from '../alfrescoApi'; import { Storage } from '../storage'; import { HttpClient } from '../api-clients/http-client.interface'; +import { PathMatcher } from '../utils/path-matcher'; declare const Buffer: any; -declare const require: any; -// tslint:disable-next-line -const minimatch = require('minimatch'); declare let window: Window; @@ -228,7 +226,7 @@ export class Oauth2Auth extends AlfrescoApiClient { if (Array.isArray(publicUrls)) { return publicUrls.length > 0 && - publicUrls.some((urlPattern: string) => minimatch(window.location.href, urlPattern)); + publicUrls.some((urlPattern: string) => PathMatcher.match(window.location.href, urlPattern)); } return false; } diff --git a/src/utils/path-matcher.ts b/src/utils/path-matcher.ts new file mode 100644 index 0000000000..f9a3ca4b35 --- /dev/null +++ b/src/utils/path-matcher.ts @@ -0,0 +1,12 @@ +export class PathMatcher { + static match(path: string, pattern: string) { + return new RegExp( + `^${ + pattern + .replace(/(^|[^\*])\*(?!\*)/g, '$1([^\\/]*)') + .replace(/\/\*\*\//g, '/(.+)/|/') + .replace(/\*\*/g, '(.*)') + }$` + ).test(path); + } +} diff --git a/test/oauth2Auth.spec.ts b/test/oauth2Auth.spec.ts index ff3af5c450..dd46c3ca1d 100644 --- a/test/oauth2Auth.spec.ts +++ b/test/oauth2Auth.spec.ts @@ -10,6 +10,7 @@ const spies = require('chai-spies'); chai.use(spies); import { EcmAuthMock, OAuthMock } from '../test/mockObjects'; +import { PathMatcher } from '../src/utils/path-matcher'; const jsdom = require('mocha-jsdom'); const globalAny: any = global; @@ -546,53 +547,54 @@ describe('Oauth2 test', () => { ); }); - it('should return `true` if url is defined in public urls list', () => { + it('should return true if PathMatcher.match returns true for matching url', () => { globalAny.window = { location: { href: 'public-url' } }; oauth2Auth.config.oauth2.publicUrls = ['public-url']; + chai.spy.on(PathMatcher, 'match', () => true); - expect(oauth2Auth.isPublicUrl()).to.be.equal(true); + expect(oauth2Auth.isPublicUrl()).be.true; + expect(PathMatcher.match).called.with(globalAny.window.location.href, oauth2Auth.config.oauth2.publicUrls[0]); }); - it('should return `false` if url is not defined in public urls list', () => { + it('should return false if PathMatcher.match returns false for matching url', () => { globalAny.window = { location: { href: 'some-public-url' } }; oauth2Auth.config.oauth2.publicUrls = ['public-url']; + chai.spy.on(PathMatcher, 'match', () => false); - expect(oauth2Auth.isPublicUrl()).to.be.equal(false); + expect(oauth2Auth.isPublicUrl()).be.false; + expect(PathMatcher.match).called.with(globalAny.window.location.href, oauth2Auth.config.oauth2.publicUrls[0]); }); - it('should return `false` if publicUrls property is not defined', () => { - expect(oauth2Auth.isPublicUrl()).to.be.equal(false); + it('should return false if publicUrls property is not defined', () => { + chai.spy.on(PathMatcher, 'match'); + + expect(oauth2Auth.isPublicUrl()).be.false; + expect(PathMatcher.match).not.called(); }); - it('should return `false` if public urls is not set as an array list', () => { + it('should return false if public urls is not set as an array list', () => { globalAny.window = { location: { href: 'public-url-string' } }; oauth2Auth.config.oauth2.publicUrls = null; + chai.spy.on(PathMatcher, 'match'); - expect(oauth2Auth.isPublicUrl()).to.be.equal(false); - }); - - it('should match absolute path', () => { - globalAny.window = { location: { href: 'http://some-public-url' } }; - oauth2Auth.config.oauth2.publicUrls = ['http://some-public-url']; - - expect(oauth2Auth.isPublicUrl()).to.be.equal(true); - }); - - it('should match a path pattern', () => { - globalAny.window = { location: { href: 'http://some-public-url/123/path' } }; - oauth2Auth.config.oauth2.publicUrls = ['**/some-public-url/*/path']; - - expect(oauth2Auth.isPublicUrl()).to.be.equal(true); + expect(oauth2Auth.isPublicUrl()).be.false; + expect(PathMatcher.match).not.called(); }); it('should not call `implicitLogin`', async () => { globalAny.window = { location: { href: 'public-url' } }; oauth2Auth.config.oauth2.silentLogin = true; oauth2Auth.config.oauth2.publicUrls = ['public-url']; + chai.spy.on(PathMatcher, 'match', () => true); const implicitLoginSpy = chai.spy.on(oauth2Auth, 'implicitLogin'); await oauth2Auth.checkFragment(); expect(implicitLoginSpy).not.to.have.been.called(); + expect(PathMatcher.match).called.with(globalAny.window.location.href, oauth2Auth.config.oauth2.publicUrls[0]); + }); + + afterEach(() => { + chai.spy.restore(PathMatcher, 'match'); }); }); }); diff --git a/test/path-matcher.spec.ts b/test/path-matcher.spec.ts new file mode 100644 index 0000000000..c6785e000b --- /dev/null +++ b/test/path-matcher.spec.ts @@ -0,0 +1,84 @@ +import { PathMatcher } from '../src/utils/path-matcher'; + +const chai = require('chai'); +const expect = chai.expect; + +describe('PathMatcher', () => { + describe('match', () => { + it('should return true if path is exactly the same like pattern', () => { + expect(PathMatcher.match('public-url', 'public-url')).be.true; + }); + + it('should return false if path is not equal to pattern', () => { + expect(PathMatcher.match('some-public-url', 'public-url')).be.false; + }); + + it('should return true if absolute path is equal to absolute path', () => { + expect(PathMatcher.match('http://some-public-url', 'http://some-public-url')).be.true; + }); + + it('should return true if path matches pattern containing double and single *', () => { + expect(PathMatcher.match('http://some-public-url/123/path', '**/some-public-url/*/path')).be.true; + }); + + it('should return true if path matches to pattern after replacing ** with multiple parts at the beginning', () => { + expect(PathMatcher.match('http://test/other-test/some-public-url/path', '**/some-public-url/path')).be.true; + }); + + it('should return true if path matches to pattern after replacing ** with multiple parts at the beginning', () => { + expect(PathMatcher.match('http://test/other-test/some-public-url/path', '**/some-public-url/path')).be.true; + }); + + it('should return true if path matches to pattern after replacing ** with multiple parts at the end', () => { + expect(PathMatcher.match('http://some-public-url/path/test/other-test', 'http://some-public-url/path/**')).be.true; + }); + + it('should return true if path matches to pattern after replacing ** with none parts at the end', () => { + expect(PathMatcher.match('http://some-public-url/path/', 'http://some-public-url/path/**')).be.true; + }); + + it('should return false if path does not match to pattern after replacing ** with none parts at the end and cuts last /', () => { + expect(PathMatcher.match('http://some-public-url/path', 'http://some-public-url/path/**')).be.false; + }); + + it('should return true if path matches to pattern after replacing ** with multiple parts in the middle', () => { + expect(PathMatcher.match('http://some-public-url/test/other-test/path', 'http://some-public-url/**/path')).be.true; + }); + + it('should return true if path matches to pattern after replacing ** with none parts in the middle', () => { + expect(PathMatcher.match('http://some-public-url/path', 'http://some-public-url/**/path')).be.true; + }); + + it('should return false if path does not match to pattern with **', () => { + expect(PathMatcher.match('http://some-public-url/', 'http://some-public-url/**/path')).be.false; + }); + + it('should return false if path has more than one part as replacement for * in the middle of pattern', () => { + expect(PathMatcher.match('http://some-public-url/123/test/path', 'http://some-public-url/*/path')).be.false; + }); + + it('should return false if path has zero parts as replacement for * in the middle of pattern', () => { + expect(PathMatcher.match('http://some-public-url/path', 'http://some-public-url/*/path')).be.false; + }); + + it('should return true if path matches to pattern containing * at the end', () => { + expect(PathMatcher.match('http://some-public-url/path/test', 'http://some-public-url/path/*')).be.true; + }); + + it('should return false if path matches to pattern containing * at the end and cuts last /', () => { + expect(PathMatcher.match('http://some-public-url/path', 'http://some-public-url/path/*')).be.false; + }); + + it('should return false if path has more than one part as replacement for * at the end of pattern', () => { + expect(PathMatcher.match('http://some-public-url/path/test/other-test', 'http://some-public-url/path/*')).be.false; + }); + + it('should return false if path has zero parts as replacement for * at the end of pattern', () => { + expect(PathMatcher.match('http://some-public-url/path/test/other-test', 'http://some-public-url/path/*')).be.false; + }); + + it('should return false if path starts with http:// and * is at the beginning of pattern', () => { + expect(PathMatcher.match('http://some-public-url/path/test', '*/some-public-url/path')).be.false; + }); + }); +});