mirror of
https://github.com/sasjs/server.git
synced 2025-12-12 11:54:35 +00:00
Compare commits
3 Commits
v0.22.1
...
domain-che
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ed10109c1 | ||
|
|
ef9cca575f | ||
|
|
efbfd3f392 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,24 +1,3 @@
|
|||||||
## [0.22.1](https://github.com/sasjs/server/compare/v0.22.0...v0.22.1) (2022-10-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* spelling issues ([3bb0597](https://github.com/sasjs/server/commit/3bb05974d216d69368f4498eb9f309bce7d97fd8))
|
|
||||||
|
|
||||||
# [0.22.0](https://github.com/sasjs/server/compare/v0.21.7...v0.22.0) (2022-10-03)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* do not throw error on deleting group when it is created by an external auth provider ([68f0c5c](https://github.com/sasjs/server/commit/68f0c5c5884431e7e8f586dccf98132abebb193e))
|
|
||||||
* no need to restrict api endpoints when ldap auth is applied ([a142660](https://github.com/sasjs/server/commit/a14266077d3541c7a33b7635efa4208335e73519))
|
|
||||||
* remove authProvider attribute from user and group payload interface ([bbd7786](https://github.com/sasjs/server/commit/bbd7786c6ce13b374d896a45c23255b8fa3e8bd2))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* implemented LDAP authentication ([f915c51](https://github.com/sasjs/server/commit/f915c51b077a2b8c4099727355ed914ecd6364bd))
|
|
||||||
|
|
||||||
## [0.21.7](https://github.com/sasjs/server/compare/v0.21.6...v0.21.7) (2022-09-30)
|
## [0.21.7](https://github.com/sasjs/server/compare/v0.21.6...v0.21.7) (2022-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -125,19 +125,9 @@ PRIVATE_KEY=privkey.pem (required)
|
|||||||
CERT_CHAIN=certificate.pem (required)
|
CERT_CHAIN=certificate.pem (required)
|
||||||
CA_ROOT=fullchain.pem (optional)
|
CA_ROOT=fullchain.pem (optional)
|
||||||
|
|
||||||
## ENV variables required for MODE: `server`
|
# ENV variables required for MODE: `server`
|
||||||
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
||||||
|
|
||||||
# AUTH_PROVIDERS options: [ldap] default: ``
|
|
||||||
AUTH_PROVIDERS=
|
|
||||||
|
|
||||||
## ENV variables required for AUTH_MECHANISM: `ldap`
|
|
||||||
LDAP_URL= <LDAP_SERVER_URL>
|
|
||||||
LDAP_BIND_DN= <cn=admin,ou=system,dc=cloudron>
|
|
||||||
LDAP_BIND_PASSWORD = <password>
|
|
||||||
LDAP_USERS_BASE_DN = <ou=users,dc=cloudron>
|
|
||||||
LDAP_GROUPS_BASE_DN = <ou=groups,dc=cloudron>
|
|
||||||
|
|
||||||
# options: [disable|enable] default: `disable` for `server` & `enable` for `desktop`
|
# options: [disable|enable] default: `disable` for `server` & `enable` for `desktop`
|
||||||
# If enabled, be sure to also configure the WHITELIST of third party servers.
|
# If enabled, be sure to also configure the WHITELIST of third party servers.
|
||||||
CORS=
|
CORS=
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
MODE=[desktop|server] default considered as desktop
|
MODE=[desktop|server] default considered as desktop
|
||||||
CORS=[disable|enable] default considered as disable for server MODE & enable for desktop MODE
|
CORS=[disable|enable] default considered as disable for server MODE & enable for desktop MODE
|
||||||
|
ALLOWED_DOMAINS=<space separated urls, each starting with protocol `http` or `https`>
|
||||||
WHITELIST=<space separated urls, each starting with protocol `http` or `https`>
|
WHITELIST=<space separated urls, each starting with protocol `http` or `https`>
|
||||||
|
|
||||||
PROTOCOL=[http|https] default considered as http
|
PROTOCOL=[http|https] default considered as http
|
||||||
@@ -14,14 +15,6 @@ HELMET_COEP=[true|false] if omitted HELMET default will be used
|
|||||||
|
|
||||||
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
DB_CONNECT=mongodb+srv://<DB_USERNAME>:<DB_PASSWORD>@<CLUSTER>/<DB_NAME>?retryWrites=true&w=majority
|
||||||
|
|
||||||
AUTH_PROVIDERS=[ldap|internal] default considered as internal
|
|
||||||
|
|
||||||
LDAP_URL= <LDAP_SERVER_URL>
|
|
||||||
LDAP_BIND_DN= <cn=admin,ou=system,dc=cloudron>
|
|
||||||
LDAP_BIND_PASSWORD = <password>
|
|
||||||
LDAP_USERS_BASE_DN = <ou=users,dc=cloudron>
|
|
||||||
LDAP_GROUPS_BASE_DN = <ou=groups,dc=cloudron>
|
|
||||||
|
|
||||||
RUN_TIMES=[sas,js,py | js,py | sas | sas,js] default considered as sas
|
RUN_TIMES=[sas,js,py | js,py | sas | sas,js] default considered as sas
|
||||||
SAS_PATH=/opt/sas/sas9/SASHome/SASFoundation/9.4/sas
|
SAS_PATH=/opt/sas/sas9/SASHome/SASFoundation/9.4/sas
|
||||||
NODE_PATH=~/.nvm/versions/node/v16.14.0/bin/node
|
NODE_PATH=~/.nvm/versions/node/v16.14.0/bin/node
|
||||||
|
|||||||
223
api/package-lock.json
generated
223
api/package-lock.json
generated
@@ -19,7 +19,6 @@
|
|||||||
"helmet": "^5.0.2",
|
"helmet": "^5.0.2",
|
||||||
"joi": "^17.4.2",
|
"joi": "^17.4.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"ldapjs": "2.3.3",
|
|
||||||
"mongoose": "^6.0.12",
|
"mongoose": "^6.0.12",
|
||||||
"mongoose-sequence": "^5.3.1",
|
"mongoose-sequence": "^5.3.1",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
@@ -41,7 +40,6 @@
|
|||||||
"@types/express-session": "^1.17.4",
|
"@types/express-session": "^1.17.4",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
"@types/jsonwebtoken": "^8.5.5",
|
"@types/jsonwebtoken": "^8.5.5",
|
||||||
"@types/ldapjs": "^2.2.4",
|
|
||||||
"@types/mongoose-sequence": "^3.0.6",
|
"@types/mongoose-sequence": "^3.0.6",
|
||||||
"@types/morgan": "^1.9.3",
|
"@types/morgan": "^1.9.3",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
@@ -2036,15 +2034,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/ldapjs": {
|
|
||||||
"version": "2.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.4.tgz",
|
|
||||||
"integrity": "sha512-+ZMVolW4N1zpnQ4SgH8nfpIFuiDOfbnXSbwQoBiLaq8mF0vo8FOKotQzKkfoWxbV0lWU1d4V+keZZ07klyOSng==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/mime": {
|
"node_modules/@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
@@ -2220,11 +2209,6 @@
|
|||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/abstract-logging": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
|
|
||||||
},
|
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
@@ -2480,14 +2464,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/asn1": {
|
|
||||||
"version": "0.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"safer-buffer": "~2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/asn1.js": {
|
"node_modules/asn1.js": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
||||||
@@ -2499,14 +2475,6 @@
|
|||||||
"safer-buffer": "^2.1.0"
|
"safer-buffer": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/assert-plus": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "2.6.4",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
@@ -2668,17 +2636,6 @@
|
|||||||
"@babel/core": "^7.0.0"
|
"@babel/core": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/backoff": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
|
|
||||||
"dependencies": {
|
|
||||||
"precond": "0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -4049,14 +4006,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/extsprintf": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.2.11",
|
"version": "3.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||||
@@ -6769,35 +6718,6 @@
|
|||||||
"node": ">8"
|
"node": ">8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ldap-filter": {
|
|
||||||
"version": "0.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
|
|
||||||
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ldapjs": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
|
|
||||||
"dependencies": {
|
|
||||||
"abstract-logging": "^2.0.0",
|
|
||||||
"asn1": "^0.2.4",
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"backoff": "^2.5.0",
|
|
||||||
"ldap-filter": "^0.3.3",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"vasync": "^2.2.0",
|
|
||||||
"verror": "^1.8.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/leven": {
|
"node_modules/leven": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||||
@@ -8087,14 +8007,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/precond": {
|
|
||||||
"version": "0.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
|
|
||||||
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||||
@@ -9693,43 +9605,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vasync": {
|
|
||||||
"version": "2.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"verror": "1.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vasync/node_modules/verror": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
|
||||||
"engines": [
|
|
||||||
"node >=0.6.0"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/verror": {
|
|
||||||
"version": "1.10.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
|
||||||
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
|
|
||||||
"dependencies": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/w3c-hr-time": {
|
"node_modules/w3c-hr-time": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||||
@@ -11622,15 +11497,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/ldapjs": {
|
|
||||||
"version": "2.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.4.tgz",
|
|
||||||
"integrity": "sha512-+ZMVolW4N1zpnQ4SgH8nfpIFuiDOfbnXSbwQoBiLaq8mF0vo8FOKotQzKkfoWxbV0lWU1d4V+keZZ07klyOSng==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/mime": {
|
"@types/mime": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||||
@@ -11805,11 +11671,6 @@
|
|||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"abstract-logging": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
|
|
||||||
},
|
|
||||||
"accepts": {
|
"accepts": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
@@ -12008,14 +11869,6 @@
|
|||||||
"is-string": "^1.0.7"
|
"is-string": "^1.0.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"asn1": {
|
|
||||||
"version": "0.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
|
||||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
|
||||||
"requires": {
|
|
||||||
"safer-buffer": "~2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"asn1.js": {
|
"asn1.js": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
||||||
@@ -12027,11 +11880,6 @@
|
|||||||
"safer-buffer": "^2.1.0"
|
"safer-buffer": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assert-plus": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
|
|
||||||
},
|
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.4",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
@@ -12159,14 +12007,6 @@
|
|||||||
"babel-preset-current-node-syntax": "^1.0.0"
|
"babel-preset-current-node-syntax": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"backoff": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==",
|
|
||||||
"requires": {
|
|
||||||
"precond": "0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -13217,11 +13057,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extsprintf": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="
|
|
||||||
},
|
|
||||||
"fast-glob": {
|
"fast-glob": {
|
||||||
"version": "3.2.11",
|
"version": "3.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
|
||||||
@@ -15262,29 +15097,6 @@
|
|||||||
"asn1.js": "^5.4.1"
|
"asn1.js": "^5.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ldap-filter": {
|
|
||||||
"version": "0.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz",
|
|
||||||
"integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==",
|
|
||||||
"requires": {
|
|
||||||
"assert-plus": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ldapjs": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==",
|
|
||||||
"requires": {
|
|
||||||
"abstract-logging": "^2.0.0",
|
|
||||||
"asn1": "^0.2.4",
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"backoff": "^2.5.0",
|
|
||||||
"ldap-filter": "^0.3.3",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"vasync": "^2.2.0",
|
|
||||||
"verror": "^1.8.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"leven": {
|
"leven": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||||
@@ -16256,11 +16068,6 @@
|
|||||||
"tunnel-agent": "^0.6.0"
|
"tunnel-agent": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"precond": {
|
|
||||||
"version": "0.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
|
|
||||||
"integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ=="
|
|
||||||
},
|
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||||
@@ -17456,36 +17263,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
},
|
},
|
||||||
"vasync": {
|
|
||||||
"version": "2.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==",
|
|
||||||
"requires": {
|
|
||||||
"verror": "1.10.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"verror": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
|
|
||||||
"requires": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"verror": {
|
|
||||||
"version": "1.10.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
|
|
||||||
"integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
|
|
||||||
"requires": {
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"extsprintf": "^1.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"w3c-hr-time": {
|
"w3c-hr-time": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
"helmet": "^5.0.2",
|
"helmet": "^5.0.2",
|
||||||
"joi": "^17.4.2",
|
"joi": "^17.4.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"ldapjs": "2.3.3",
|
|
||||||
"mongoose": "^6.0.12",
|
"mongoose": "^6.0.12",
|
||||||
"mongoose-sequence": "^5.3.1",
|
"mongoose-sequence": "^5.3.1",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
@@ -77,7 +76,6 @@
|
|||||||
"@types/express-session": "^1.17.4",
|
"@types/express-session": "^1.17.4",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
"@types/jsonwebtoken": "^8.5.5",
|
"@types/jsonwebtoken": "^8.5.5",
|
||||||
"@types/ldapjs": "^2.2.4",
|
|
||||||
"@types/mongoose-sequence": "^3.0.6",
|
"@types/mongoose-sequence": "^3.0.6",
|
||||||
"@types/morgan": "^1.9.3",
|
"@types/morgan": "^1.9.3",
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
|
|||||||
@@ -622,51 +622,6 @@ paths:
|
|||||||
-
|
-
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
parameters: []
|
parameters: []
|
||||||
/SASjsApi/authConfig:
|
|
||||||
get:
|
|
||||||
operationId: GetDetail
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Ok
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema: {}
|
|
||||||
examples:
|
|
||||||
'Example 1':
|
|
||||||
value: {ldap: {LDAP_URL: 'ldaps://my.ldap.server:636', LDAP_BIND_DN: 'cn=admin,ou=system,dc=cloudron', LDAP_BIND_PASSWORD: secret, LDAP_USERS_BASE_DN: 'ou=users,dc=cloudron', LDAP_GROUPS_BASE_DN: 'ou=groups,dc=cloudron'}}
|
|
||||||
summary: 'Gives the detail of Auth Mechanism.'
|
|
||||||
tags:
|
|
||||||
- Auth_Config
|
|
||||||
security:
|
|
||||||
-
|
|
||||||
bearerAuth: []
|
|
||||||
parameters: []
|
|
||||||
/SASjsApi/authConfig/synchroniseWithLDAP:
|
|
||||||
post:
|
|
||||||
operationId: SynchroniseWithLDAP
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Ok
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
properties:
|
|
||||||
groupCount: {type: number, format: double}
|
|
||||||
userCount: {type: number, format: double}
|
|
||||||
required:
|
|
||||||
- groupCount
|
|
||||||
- userCount
|
|
||||||
type: object
|
|
||||||
examples:
|
|
||||||
'Example 1':
|
|
||||||
value: {users: 5, groups: 3}
|
|
||||||
summary: 'Synchronises LDAP users and groups with internal DB and returns the count of imported users and groups.'
|
|
||||||
tags:
|
|
||||||
- Auth_Config
|
|
||||||
security:
|
|
||||||
-
|
|
||||||
bearerAuth: []
|
|
||||||
parameters: []
|
|
||||||
/SASjsApi/client:
|
/SASjsApi/client:
|
||||||
post:
|
post:
|
||||||
operationId: CreateClient
|
operationId: CreateClient
|
||||||
@@ -1839,9 +1794,6 @@ tags:
|
|||||||
-
|
-
|
||||||
name: Auth
|
name: Auth
|
||||||
description: 'Operations about auth'
|
description: 'Operations about auth'
|
||||||
-
|
|
||||||
name: Auth_Config
|
|
||||||
description: 'Operations about external auth providers'
|
|
||||||
-
|
-
|
||||||
name: Client
|
name: Client
|
||||||
description: 'Operations about clients'
|
description: 'Operations about clients'
|
||||||
|
|||||||
@@ -3,10 +3,21 @@ import cors from 'cors'
|
|||||||
import { CorsType } from '../utils'
|
import { CorsType } from '../utils'
|
||||||
|
|
||||||
export const configureCors = (app: Express) => {
|
export const configureCors = (app: Express) => {
|
||||||
|
const { CORS } = process.env
|
||||||
|
|
||||||
|
if (CORS === CorsType.ENABLED) {
|
||||||
|
const whiteList = getWhiteListed()
|
||||||
|
|
||||||
|
console.log('All CORS Requests are enabled for:', whiteList)
|
||||||
|
app.use(cors({ credentials: true, origin: whiteList }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getWhiteListed = (): string[] => {
|
||||||
|
const whiteList: string[] = []
|
||||||
const { CORS, WHITELIST } = process.env
|
const { CORS, WHITELIST } = process.env
|
||||||
|
|
||||||
if (CORS === CorsType.ENABLED) {
|
if (CORS === CorsType.ENABLED) {
|
||||||
const whiteList: string[] = []
|
|
||||||
WHITELIST?.split(' ')
|
WHITELIST?.split(' ')
|
||||||
?.filter((url) => !!url)
|
?.filter((url) => !!url)
|
||||||
.forEach((url) => {
|
.forEach((url) => {
|
||||||
@@ -14,8 +25,6 @@ export const configureCors = (app: Express) => {
|
|||||||
// removing trailing slash of URLs listing for CORS
|
// removing trailing slash of URLs listing for CORS
|
||||||
whiteList.push(url.replace(/\/$/, ''))
|
whiteList.push(url.replace(/\/$/, ''))
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('All CORS Requests are enabled for:', whiteList)
|
|
||||||
app.use(cors({ credentials: true, origin: whiteList }))
|
|
||||||
}
|
}
|
||||||
|
return whiteList
|
||||||
}
|
}
|
||||||
|
|||||||
29
api/src/app-modules/configureDomains.ts
Normal file
29
api/src/app-modules/configureDomains.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Express } from 'express'
|
||||||
|
import { checkDomain } from '../middlewares'
|
||||||
|
import { getWhiteListed } from './configureCors'
|
||||||
|
|
||||||
|
export const configureDomains = (app: Express) => {
|
||||||
|
// const domains: string[] = []
|
||||||
|
const domains = new Set<string>()
|
||||||
|
const { ALLOWED_DOMAINS } = process.env
|
||||||
|
|
||||||
|
const allowedDomains = ALLOWED_DOMAINS?.trim().split(' ') ?? []
|
||||||
|
|
||||||
|
const whiteListed = getWhiteListed()
|
||||||
|
|
||||||
|
const combinedUrls = [...allowedDomains, ...whiteListed]
|
||||||
|
combinedUrls
|
||||||
|
.filter((domainName) => !!domainName)
|
||||||
|
.forEach((url) => {
|
||||||
|
try {
|
||||||
|
const domain = new URL(url)
|
||||||
|
domains.add(domain.hostname)
|
||||||
|
} catch (_) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (domains.size) {
|
||||||
|
process.allowedDomains = [...domains]
|
||||||
|
console.log('Allowed Domain(s):', process.allowedDomains)
|
||||||
|
app.use(checkDomain)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Express } from 'express'
|
import { Express, CookieOptions } from 'express'
|
||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
import session from 'express-session'
|
import session from 'express-session'
|
||||||
import MongoStore from 'connect-mongo'
|
import MongoStore from 'connect-mongo'
|
||||||
|
|
||||||
import { ModeType } from '../utils'
|
import { ModeType, ProtocolType } from '../utils'
|
||||||
import { cookieOptions } from '../app'
|
|
||||||
|
|
||||||
export const configureExpressSession = (app: Express) => {
|
export const configureExpressSession = (app: Express) => {
|
||||||
const { MODE } = process.env
|
const { MODE } = process.env
|
||||||
@@ -19,6 +18,15 @@ export const configureExpressSession = (app: Express) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { PROTOCOL } = process.env
|
||||||
|
const cookieOptions: CookieOptions = {
|
||||||
|
secure: PROTOCOL === ProtocolType.HTTPS,
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: PROTOCOL === ProtocolType.HTTPS ? 'none' : undefined,
|
||||||
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
|
domain: 'sas.4gl.io'
|
||||||
|
}
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
secret: process.secrets.SESSION_SECRET,
|
secret: process.secrets.SESSION_SECRET,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './configureCors'
|
export * from './configureCors'
|
||||||
|
export * from './configureDomains'
|
||||||
export * from './configureExpressSession'
|
export * from './configureExpressSession'
|
||||||
export * from './configureLogger'
|
export * from './configureLogger'
|
||||||
export * from './configureSecurity'
|
export * from './configureSecurity'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import express, { ErrorRequestHandler, CookieOptions } from 'express'
|
import express, { ErrorRequestHandler } from 'express'
|
||||||
import cookieParser from 'cookie-parser'
|
import cookieParser from 'cookie-parser'
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
getWebBuildFolder,
|
getWebBuildFolder,
|
||||||
instantiateLogger,
|
instantiateLogger,
|
||||||
loadAppStreamConfig,
|
loadAppStreamConfig,
|
||||||
ProtocolType,
|
|
||||||
ReturnCode,
|
ReturnCode,
|
||||||
setProcessVariables,
|
setProcessVariables,
|
||||||
setupFolders,
|
setupFolders,
|
||||||
@@ -16,6 +15,7 @@ import {
|
|||||||
} from './utils'
|
} from './utils'
|
||||||
import {
|
import {
|
||||||
configureCors,
|
configureCors,
|
||||||
|
configureDomains,
|
||||||
configureExpressSession,
|
configureExpressSession,
|
||||||
configureLogger,
|
configureLogger,
|
||||||
configureSecurity
|
configureSecurity
|
||||||
@@ -29,15 +29,6 @@ if (verifyEnvVariables()) process.exit(ReturnCode.InvalidEnv)
|
|||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
const { PROTOCOL } = process.env
|
|
||||||
|
|
||||||
export const cookieOptions: CookieOptions = {
|
|
||||||
secure: PROTOCOL === ProtocolType.HTTPS,
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: PROTOCOL === ProtocolType.HTTPS ? 'none' : undefined,
|
|
||||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
|
||||||
}
|
|
||||||
|
|
||||||
const onError: ErrorRequestHandler = (err, req, res, next) => {
|
const onError: ErrorRequestHandler = (err, req, res, next) => {
|
||||||
console.error(err.stack)
|
console.error(err.stack)
|
||||||
res.status(500).send('Something broke!')
|
res.status(500).send('Something broke!')
|
||||||
@@ -58,6 +49,11 @@ export default setProcessVariables().then(async () => {
|
|||||||
***********************************/
|
***********************************/
|
||||||
configureCors(app)
|
configureCors(app)
|
||||||
|
|
||||||
|
/***********************************
|
||||||
|
* Allowed Domains *
|
||||||
|
***********************************/
|
||||||
|
configureDomains(app)
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
* DB Connection & *
|
* DB Connection & *
|
||||||
* Express Sessions *
|
* Express Sessions *
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
import express from 'express'
|
|
||||||
import { Security, Route, Tags, Get, Post, Example } from 'tsoa'
|
|
||||||
|
|
||||||
import { LDAPClient, LDAPUser, LDAPGroup, AuthProviderType } from '../utils'
|
|
||||||
import { randomBytes } from 'crypto'
|
|
||||||
import User from '../model/User'
|
|
||||||
import Group from '../model/Group'
|
|
||||||
import Permission from '../model/Permission'
|
|
||||||
|
|
||||||
@Security('bearerAuth')
|
|
||||||
@Route('SASjsApi/authConfig')
|
|
||||||
@Tags('Auth_Config')
|
|
||||||
export class AuthConfigController {
|
|
||||||
/**
|
|
||||||
* @summary Gives the detail of Auth Mechanism.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Example({
|
|
||||||
ldap: {
|
|
||||||
LDAP_URL: 'ldaps://my.ldap.server:636',
|
|
||||||
LDAP_BIND_DN: 'cn=admin,ou=system,dc=cloudron',
|
|
||||||
LDAP_BIND_PASSWORD: 'secret',
|
|
||||||
LDAP_USERS_BASE_DN: 'ou=users,dc=cloudron',
|
|
||||||
LDAP_GROUPS_BASE_DN: 'ou=groups,dc=cloudron'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@Get('/')
|
|
||||||
public getDetail() {
|
|
||||||
return getAuthConfigDetail()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Synchronises LDAP users and groups with internal DB and returns the count of imported users and groups.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Example({
|
|
||||||
users: 5,
|
|
||||||
groups: 3
|
|
||||||
})
|
|
||||||
@Post('/synchroniseWithLDAP')
|
|
||||||
public async synchroniseWithLDAP() {
|
|
||||||
return synchroniseWithLDAP()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const synchroniseWithLDAP = async () => {
|
|
||||||
process.logger.info('Syncing LDAP with internal DB')
|
|
||||||
|
|
||||||
const permissions = await Permission.get({})
|
|
||||||
await Permission.deleteMany()
|
|
||||||
await User.deleteMany({ authProvider: AuthProviderType.LDAP })
|
|
||||||
await Group.deleteMany({ authProvider: AuthProviderType.LDAP })
|
|
||||||
|
|
||||||
const ldapClient = await LDAPClient.init()
|
|
||||||
|
|
||||||
process.logger.info('fetching LDAP users')
|
|
||||||
const users = await ldapClient.getAllLDAPUsers()
|
|
||||||
|
|
||||||
process.logger.info('inserting LDAP users to DB')
|
|
||||||
|
|
||||||
const existingUsers: string[] = []
|
|
||||||
const importedUsers: LDAPUser[] = []
|
|
||||||
|
|
||||||
for (const user of users) {
|
|
||||||
const usernameExists = await User.findOne({ username: user.username })
|
|
||||||
if (usernameExists) {
|
|
||||||
existingUsers.push(user.username)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const hashPassword = User.hashPassword(randomBytes(64).toString('hex'))
|
|
||||||
|
|
||||||
await User.create({
|
|
||||||
displayName: user.displayName,
|
|
||||||
username: user.username,
|
|
||||||
password: hashPassword,
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
|
|
||||||
importedUsers.push(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingUsers.length > 0) {
|
|
||||||
process.logger.info(
|
|
||||||
'Failed to insert following users as they already exist in DB:'
|
|
||||||
)
|
|
||||||
existingUsers.forEach((user) => process.logger.log(`* ${user}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
process.logger.info('fetching LDAP groups')
|
|
||||||
const groups = await ldapClient.getAllLDAPGroups()
|
|
||||||
|
|
||||||
process.logger.info('inserting LDAP groups to DB')
|
|
||||||
|
|
||||||
const existingGroups: string[] = []
|
|
||||||
const importedGroups: LDAPGroup[] = []
|
|
||||||
|
|
||||||
for (const group of groups) {
|
|
||||||
const groupExists = await Group.findOne({ name: group.name })
|
|
||||||
if (groupExists) {
|
|
||||||
existingGroups.push(group.name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
await Group.create({
|
|
||||||
name: group.name,
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
|
|
||||||
importedGroups.push(group)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingGroups.length > 0) {
|
|
||||||
process.logger.info(
|
|
||||||
'Failed to insert following groups as they already exist in DB:'
|
|
||||||
)
|
|
||||||
existingGroups.forEach((group) => process.logger.log(`* ${group}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
process.logger.info('associating users and groups')
|
|
||||||
|
|
||||||
for (const group of importedGroups) {
|
|
||||||
const dbGroup = await Group.findOne({ name: group.name })
|
|
||||||
if (dbGroup) {
|
|
||||||
for (const member of group.members) {
|
|
||||||
const user = importedUsers.find((user) => user.uid === member)
|
|
||||||
if (user) {
|
|
||||||
const dbUser = await User.findOne({ username: user.username })
|
|
||||||
if (dbUser) await dbGroup.addUser(dbUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process.logger.info('setting permissions')
|
|
||||||
|
|
||||||
for (const permission of permissions) {
|
|
||||||
const newPermission = new Permission({
|
|
||||||
path: permission.path,
|
|
||||||
type: permission.type,
|
|
||||||
setting: permission.setting
|
|
||||||
})
|
|
||||||
|
|
||||||
if (permission.user) {
|
|
||||||
const dbUser = await User.findOne({ username: permission.user.username })
|
|
||||||
if (dbUser) newPermission.user = dbUser._id
|
|
||||||
} else if (permission.group) {
|
|
||||||
const dbGroup = await Group.findOne({ name: permission.group.name })
|
|
||||||
if (dbGroup) newPermission.group = dbGroup._id
|
|
||||||
}
|
|
||||||
await newPermission.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
process.logger.info('LDAP synchronization completed!')
|
|
||||||
|
|
||||||
return {
|
|
||||||
userCount: importedUsers.length,
|
|
||||||
groupCount: importedGroups.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAuthConfigDetail = () => {
|
|
||||||
const { AUTH_PROVIDERS } = process.env
|
|
||||||
|
|
||||||
const returnObj: any = {}
|
|
||||||
|
|
||||||
if (AUTH_PROVIDERS === AuthProviderType.LDAP) {
|
|
||||||
const {
|
|
||||||
LDAP_URL,
|
|
||||||
LDAP_BIND_DN,
|
|
||||||
LDAP_BIND_PASSWORD,
|
|
||||||
LDAP_USERS_BASE_DN,
|
|
||||||
LDAP_GROUPS_BASE_DN
|
|
||||||
} = process.env
|
|
||||||
|
|
||||||
returnObj.ldap = {
|
|
||||||
LDAP_URL: LDAP_URL ?? '',
|
|
||||||
LDAP_BIND_DN: LDAP_BIND_DN ?? '',
|
|
||||||
LDAP_BIND_PASSWORD: LDAP_BIND_PASSWORD ?? '',
|
|
||||||
LDAP_USERS_BASE_DN: LDAP_USERS_BASE_DN ?? '',
|
|
||||||
LDAP_GROUPS_BASE_DN: LDAP_GROUPS_BASE_DN ?? ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnObj
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
|
|
||||||
import Group, { GroupPayload, PUBLIC_GROUP_NAME } from '../model/Group'
|
import Group, { GroupPayload, PUBLIC_GROUP_NAME } from '../model/Group'
|
||||||
import User from '../model/User'
|
import User from '../model/User'
|
||||||
import { AuthProviderType } from '../utils'
|
|
||||||
import { UserResponse } from './user'
|
import { UserResponse } from './user'
|
||||||
|
|
||||||
export interface GroupResponse {
|
export interface GroupResponse {
|
||||||
@@ -148,14 +147,12 @@ export class GroupController {
|
|||||||
@Delete('{groupId}')
|
@Delete('{groupId}')
|
||||||
public async deleteGroup(@Path() groupId: number) {
|
public async deleteGroup(@Path() groupId: number) {
|
||||||
const group = await Group.findOne({ groupId })
|
const group = await Group.findOne({ groupId })
|
||||||
if (!group)
|
if (group) return await group.remove()
|
||||||
throw {
|
throw {
|
||||||
code: 404,
|
code: 404,
|
||||||
status: 'Not Found',
|
status: 'Not Found',
|
||||||
message: 'Group not found.'
|
message: 'Group not found.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return await group.remove()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,13 +248,6 @@ const updateUsersListInGroup = async (
|
|||||||
message: `Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
|
message: `Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.authProvider !== AuthProviderType.Internal)
|
|
||||||
throw {
|
|
||||||
code: 405,
|
|
||||||
status: 'Method Not Allowed',
|
|
||||||
message: `Can't add/remove user to group created by external auth provider.`
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await User.findOne({ id: userId })
|
const user = await User.findOne({ id: userId })
|
||||||
if (!user)
|
if (!user)
|
||||||
throw {
|
throw {
|
||||||
@@ -266,13 +256,6 @@ const updateUsersListInGroup = async (
|
|||||||
message: 'User not found.'
|
message: 'User not found.'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.authProvider !== AuthProviderType.Internal)
|
|
||||||
throw {
|
|
||||||
code: 405,
|
|
||||||
status: 'Method Not Allowed',
|
|
||||||
message: `Can't add/remove user to group created by external auth provider.`
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedGroup =
|
const updatedGroup =
|
||||||
action === 'addUser'
|
action === 'addUser'
|
||||||
? await group.addUser(user)
|
? await group.addUser(user)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export * from './auth'
|
export * from './auth'
|
||||||
export * from './authConfig'
|
|
||||||
export * from './client'
|
export * from './client'
|
||||||
export * from './code'
|
export * from './code'
|
||||||
export * from './drive'
|
export * from './drive'
|
||||||
|
|||||||
@@ -17,12 +17,7 @@ import {
|
|||||||
import { desktopUser } from '../middlewares'
|
import { desktopUser } from '../middlewares'
|
||||||
|
|
||||||
import User, { UserPayload } from '../model/User'
|
import User, { UserPayload } from '../model/User'
|
||||||
import {
|
import { getUserAutoExec, updateUserAutoExec, ModeType } from '../utils'
|
||||||
getUserAutoExec,
|
|
||||||
updateUserAutoExec,
|
|
||||||
ModeType,
|
|
||||||
AuthProviderType
|
|
||||||
} from '../utils'
|
|
||||||
import { GroupResponse } from './group'
|
import { GroupResponse } from './group'
|
||||||
|
|
||||||
export interface UserResponse {
|
export interface UserResponse {
|
||||||
@@ -216,11 +211,7 @@ const createUser = async (data: UserPayload): Promise<UserDetailsResponse> => {
|
|||||||
|
|
||||||
// Checking if user is already in the database
|
// Checking if user is already in the database
|
||||||
const usernameExist = await User.findOne({ username })
|
const usernameExist = await User.findOne({ username })
|
||||||
if (usernameExist)
|
if (usernameExist) throw new Error('Username already exists.')
|
||||||
throw {
|
|
||||||
code: 409,
|
|
||||||
message: 'Username already exists.'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash passwords
|
// Hash passwords
|
||||||
const hashPassword = User.hashPassword(password)
|
const hashPassword = User.hashPassword(password)
|
||||||
@@ -264,11 +255,7 @@ const getUser = async (
|
|||||||
'groupId name description -_id'
|
'groupId name description -_id'
|
||||||
)) as unknown as UserDetailsResponse
|
)) as unknown as UserDetailsResponse
|
||||||
|
|
||||||
if (!user)
|
if (!user) throw new Error('User is not found.')
|
||||||
throw {
|
|
||||||
code: 404,
|
|
||||||
message: 'User is not found.'
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
@@ -297,19 +284,6 @@ const updateUser = async (
|
|||||||
|
|
||||||
const params: any = { displayName, isAdmin, isActive, autoExec }
|
const params: any = { displayName, isAdmin, isActive, autoExec }
|
||||||
|
|
||||||
const user = await User.findOne(findBy)
|
|
||||||
|
|
||||||
if (
|
|
||||||
user?.authProvider !== AuthProviderType.Internal &&
|
|
||||||
(username !== user?.username || displayName !== user?.displayName)
|
|
||||||
) {
|
|
||||||
throw {
|
|
||||||
code: 405,
|
|
||||||
message:
|
|
||||||
'Can not update username and display name of user that is created by an external auth provider.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (username) {
|
if (username) {
|
||||||
// Checking if user is already in the database
|
// Checking if user is already in the database
|
||||||
const usernameExist = await User.findOne({ username })
|
const usernameExist = await User.findOne({ username })
|
||||||
@@ -318,10 +292,7 @@ const updateUser = async (
|
|||||||
(findBy.id && usernameExist.id != findBy.id) ||
|
(findBy.id && usernameExist.id != findBy.id) ||
|
||||||
(findBy.username && usernameExist.username != findBy.username)
|
(findBy.username && usernameExist.username != findBy.username)
|
||||||
)
|
)
|
||||||
throw {
|
throw new Error('Username already exists.')
|
||||||
code: 409,
|
|
||||||
message: 'Username already exists.'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
params.username = username
|
params.username = username
|
||||||
}
|
}
|
||||||
@@ -334,10 +305,7 @@ const updateUser = async (
|
|||||||
const updatedUser = await User.findOneAndUpdate(findBy, params, { new: true })
|
const updatedUser = await User.findOneAndUpdate(findBy, params, { new: true })
|
||||||
|
|
||||||
if (!updatedUser)
|
if (!updatedUser)
|
||||||
throw {
|
throw new Error(`Unable to find user with ${findBy.id || findBy.username}`)
|
||||||
code: 404,
|
|
||||||
message: `Unable to find user with ${findBy.id || findBy.username}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: updatedUser.id,
|
id: updatedUser.id,
|
||||||
@@ -364,19 +332,11 @@ const deleteUser = async (
|
|||||||
{ password }: { password?: string }
|
{ password }: { password?: string }
|
||||||
) => {
|
) => {
|
||||||
const user = await User.findOne(findBy)
|
const user = await User.findOne(findBy)
|
||||||
if (!user)
|
if (!user) throw new Error('User is not found.')
|
||||||
throw {
|
|
||||||
code: 404,
|
|
||||||
message: 'User is not found.'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
const validPass = user.comparePassword(password!)
|
const validPass = user.comparePassword(password!)
|
||||||
if (!validPass)
|
if (!validPass) throw new Error('Invalid password.')
|
||||||
throw {
|
|
||||||
code: 401,
|
|
||||||
message: 'Invalid password.'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await User.deleteOne(findBy)
|
await User.deleteOne(findBy)
|
||||||
|
|||||||
@@ -5,12 +5,7 @@ import { readFile } from '@sasjs/utils'
|
|||||||
|
|
||||||
import User from '../model/User'
|
import User from '../model/User'
|
||||||
import Client from '../model/Client'
|
import Client from '../model/Client'
|
||||||
import {
|
import { getWebBuildFolder, generateAuthCode } from '../utils'
|
||||||
getWebBuildFolder,
|
|
||||||
generateAuthCode,
|
|
||||||
AuthProviderType,
|
|
||||||
LDAPClient
|
|
||||||
} from '../utils'
|
|
||||||
import { InfoJWT } from '../types'
|
import { InfoJWT } from '../types'
|
||||||
import { AuthController } from './auth'
|
import { AuthController } from './auth'
|
||||||
|
|
||||||
@@ -85,16 +80,8 @@ const login = async (
|
|||||||
const user = await User.findOne({ username })
|
const user = await User.findOne({ username })
|
||||||
if (!user) throw new Error('Username is not found.')
|
if (!user) throw new Error('Username is not found.')
|
||||||
|
|
||||||
if (
|
|
||||||
process.env.AUTH_PROVIDERS === AuthProviderType.LDAP &&
|
|
||||||
user.authProvider === AuthProviderType.LDAP
|
|
||||||
) {
|
|
||||||
const ldapClient = await LDAPClient.init()
|
|
||||||
await ldapClient.verifyUser(username, password)
|
|
||||||
} else {
|
|
||||||
const validPass = user.comparePassword(password)
|
const validPass = user.comparePassword(password)
|
||||||
if (!validPass) throw new Error('Invalid password.')
|
if (!validPass) throw new Error('Invalid password.')
|
||||||
}
|
|
||||||
|
|
||||||
req.session.loggedIn = true
|
req.session.loggedIn = true
|
||||||
req.session.user = {
|
req.session.user = {
|
||||||
|
|||||||
17
api/src/middlewares/checkDomain.ts
Normal file
17
api/src/middlewares/checkDomain.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { RequestHandler } from 'express'
|
||||||
|
|
||||||
|
export const checkDomain: RequestHandler = (req, res, next) => {
|
||||||
|
const { allowedDomains } = process
|
||||||
|
|
||||||
|
// pass if no allowed domain is specified
|
||||||
|
if (!allowedDomains.length) return next()
|
||||||
|
|
||||||
|
if (allowedDomains.includes(req.hostname)) return next()
|
||||||
|
|
||||||
|
console.log('allowedDomains', allowedDomains)
|
||||||
|
console.log('hostname not allowed', req.hostname)
|
||||||
|
res.writeHead(404, {
|
||||||
|
'Content-Type': 'text/plain'
|
||||||
|
})
|
||||||
|
return res.end('Not found')
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from './authenticateToken'
|
export * from './authenticateToken'
|
||||||
export * from './authorize'
|
export * from './authorize'
|
||||||
|
export * from './checkDomain'
|
||||||
export * from './csrfProtection'
|
export * from './csrfProtection'
|
||||||
export * from './desktop'
|
export * from './desktop'
|
||||||
export * from './verifyAdmin'
|
export * from './verifyAdmin'
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import mongoose, { Schema, model, Document, Model } from 'mongoose'
|
import mongoose, { Schema, model, Document, Model } from 'mongoose'
|
||||||
import { GroupDetailsResponse } from '../controllers'
|
import { GroupDetailsResponse } from '../controllers'
|
||||||
import User, { IUser } from './User'
|
import User, { IUser } from './User'
|
||||||
import { AuthProviderType } from '../utils'
|
|
||||||
const AutoIncrement = require('mongoose-sequence')(mongoose)
|
const AutoIncrement = require('mongoose-sequence')(mongoose)
|
||||||
|
|
||||||
export const PUBLIC_GROUP_NAME = 'Public'
|
export const PUBLIC_GROUP_NAME = 'Public'
|
||||||
@@ -28,7 +27,6 @@ interface IGroupDocument extends GroupPayload, Document {
|
|||||||
groupId: number
|
groupId: number
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
users: Schema.Types.ObjectId[]
|
users: Schema.Types.ObjectId[]
|
||||||
authProvider?: AuthProviderType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IGroup extends IGroupDocument {
|
interface IGroup extends IGroupDocument {
|
||||||
@@ -48,11 +46,6 @@ const groupSchema = new Schema<IGroupDocument>({
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'Group description.'
|
default: 'Group description.'
|
||||||
},
|
},
|
||||||
authProvider: {
|
|
||||||
type: String,
|
|
||||||
enum: AuthProviderType,
|
|
||||||
default: 'internal'
|
|
||||||
},
|
|
||||||
isActive: {
|
isActive: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import mongoose, { Schema, model, Document, Model } from 'mongoose'
|
import mongoose, { Schema, model, Document, Model } from 'mongoose'
|
||||||
const AutoIncrement = require('mongoose-sequence')(mongoose)
|
const AutoIncrement = require('mongoose-sequence')(mongoose)
|
||||||
import bcrypt from 'bcryptjs'
|
import bcrypt from 'bcryptjs'
|
||||||
import { AuthProviderType } from '../utils'
|
|
||||||
|
|
||||||
export interface UserPayload {
|
export interface UserPayload {
|
||||||
/**
|
/**
|
||||||
@@ -43,7 +42,6 @@ interface IUserDocument extends UserPayload, Document {
|
|||||||
autoExec: string
|
autoExec: string
|
||||||
groups: Schema.Types.ObjectId[]
|
groups: Schema.Types.ObjectId[]
|
||||||
tokens: [{ [key: string]: string }]
|
tokens: [{ [key: string]: string }]
|
||||||
authProvider?: AuthProviderType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUser extends IUserDocument {
|
export interface IUser extends IUserDocument {
|
||||||
@@ -69,11 +67,6 @@ const userSchema = new Schema<IUserDocument>({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
authProvider: {
|
|
||||||
type: String,
|
|
||||||
enum: AuthProviderType,
|
|
||||||
default: 'internal'
|
|
||||||
},
|
|
||||||
isAdmin: {
|
isAdmin: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import express from 'express'
|
|
||||||
import { AuthConfigController } from '../../controllers'
|
|
||||||
const authConfigRouter = express.Router()
|
|
||||||
|
|
||||||
authConfigRouter.get('/', async (req, res) => {
|
|
||||||
const controller = new AuthConfigController()
|
|
||||||
try {
|
|
||||||
const response = controller.getDetail()
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(500).send(err.toString())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
authConfigRouter.post('/synchroniseWithLDAP', async (req, res) => {
|
|
||||||
const controller = new AuthConfigController()
|
|
||||||
try {
|
|
||||||
const response = await controller.synchroniseWithLDAP()
|
|
||||||
res.send(response)
|
|
||||||
} catch (err: any) {
|
|
||||||
res.status(500).send(err.toString())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default authConfigRouter
|
|
||||||
@@ -18,7 +18,11 @@ groupRouter.post(
|
|||||||
const response = await controller.createGroup(body)
|
const response = await controller.createGroup(body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -29,7 +33,11 @@ groupRouter.get('/', authenticateAccessToken, async (req, res) => {
|
|||||||
const response = await controller.getAllGroups()
|
const response = await controller.getAllGroups()
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -41,7 +49,11 @@ groupRouter.get('/:groupId', authenticateAccessToken, async (req, res) => {
|
|||||||
const response = await controller.getGroup(parseInt(groupId))
|
const response = await controller.getGroup(parseInt(groupId))
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -59,7 +71,11 @@ groupRouter.get(
|
|||||||
const response = await controller.getGroupByGroupName(name)
|
const response = await controller.getGroupByGroupName(name)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -79,7 +95,11 @@ groupRouter.post(
|
|||||||
)
|
)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -99,7 +119,11 @@ groupRouter.delete(
|
|||||||
)
|
)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -116,7 +140,11 @@ groupRouter.delete(
|
|||||||
await controller.deleteGroup(parseInt(groupId))
|
await controller.deleteGroup(parseInt(groupId))
|
||||||
res.status(200).send('Group Deleted!')
|
res.status(200).send('Group Deleted!')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
const statusCode = err.code
|
||||||
|
|
||||||
|
delete err.code
|
||||||
|
|
||||||
|
res.status(statusCode).send(err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import clientRouter from './client'
|
|||||||
import authRouter from './auth'
|
import authRouter from './auth'
|
||||||
import sessionRouter from './session'
|
import sessionRouter from './session'
|
||||||
import permissionRouter from './permission'
|
import permissionRouter from './permission'
|
||||||
import authConfigRouter from './authConfig'
|
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
@@ -44,14 +43,6 @@ router.use(
|
|||||||
permissionRouter
|
permissionRouter
|
||||||
)
|
)
|
||||||
|
|
||||||
router.use(
|
|
||||||
'/authConfig',
|
|
||||||
desktopRestrict,
|
|
||||||
authenticateAccessToken,
|
|
||||||
verifyAdmin,
|
|
||||||
authConfigRouter
|
|
||||||
)
|
|
||||||
|
|
||||||
router.use(
|
router.use(
|
||||||
'/',
|
'/',
|
||||||
swaggerUi.serve,
|
swaggerUi.serve,
|
||||||
|
|||||||
@@ -4,13 +4,8 @@ import { MongoMemoryServer } from 'mongodb-memory-server'
|
|||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import appPromise from '../../../app'
|
import appPromise from '../../../app'
|
||||||
import { UserController, GroupController } from '../../../controllers/'
|
import { UserController, GroupController } from '../../../controllers/'
|
||||||
import {
|
import { generateAccessToken, saveTokensInDB } from '../../../utils'
|
||||||
generateAccessToken,
|
import { PUBLIC_GROUP_NAME } from '../../../model/Group'
|
||||||
saveTokensInDB,
|
|
||||||
AuthProviderType
|
|
||||||
} from '../../../utils'
|
|
||||||
import Group, { PUBLIC_GROUP_NAME } from '../../../model/Group'
|
|
||||||
import User from '../../../model/User'
|
|
||||||
|
|
||||||
const clientId = 'someclientID'
|
const clientId = 'someclientID'
|
||||||
const adminUser = {
|
const adminUser = {
|
||||||
@@ -565,46 +560,6 @@ describe('group', () => {
|
|||||||
`Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
|
`Can't add/remove user to '${PUBLIC_GROUP_NAME}' group.`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Method Not Allowed if group is created by an external authProvider', async () => {
|
|
||||||
const dbGroup = await Group.create({
|
|
||||||
...group,
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
const dbUser = await userController.createUser({
|
|
||||||
...user,
|
|
||||||
username: 'ldapGroupUser'
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await request(app)
|
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
|
||||||
.send()
|
|
||||||
.expect(405)
|
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
|
||||||
`Can't add/remove user to group created by external auth provider.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should respond with Method Not Allowed if user is created by an external authProvider', async () => {
|
|
||||||
const dbGroup = await groupController.createGroup(group)
|
|
||||||
const dbUser = await User.create({
|
|
||||||
...user,
|
|
||||||
username: 'ldapUser',
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await request(app)
|
|
||||||
.post(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
|
||||||
.send()
|
|
||||||
.expect(405)
|
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
|
||||||
`Can't add/remove user to group created by external auth provider.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('RemoveUser', () => {
|
describe('RemoveUser', () => {
|
||||||
@@ -656,46 +611,6 @@ describe('group', () => {
|
|||||||
expect(res.body.groups).toEqual([])
|
expect(res.body.groups).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Method Not Allowed if group is created by an external authProvider', async () => {
|
|
||||||
const dbGroup = await Group.create({
|
|
||||||
...group,
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
const dbUser = await userController.createUser({
|
|
||||||
...user,
|
|
||||||
username: 'removeLdapGroupUser'
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await request(app)
|
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
|
||||||
.send()
|
|
||||||
.expect(405)
|
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
|
||||||
`Can't add/remove user to group created by external auth provider.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should respond with Method Not Allowed if user is created by an external authProvider', async () => {
|
|
||||||
const dbGroup = await groupController.createGroup(group)
|
|
||||||
const dbUser = await User.create({
|
|
||||||
...user,
|
|
||||||
username: 'removeLdapUser',
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
|
|
||||||
const res = await request(app)
|
|
||||||
.delete(`/SASjsApi/group/${dbGroup.groupId}/${dbUser.id}`)
|
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
|
||||||
.send()
|
|
||||||
.expect(405)
|
|
||||||
|
|
||||||
expect(res.text).toEqual(
|
|
||||||
`Can't add/remove user to group created by external auth provider.`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should respond with Unauthorized if access token is not present', async () => {
|
it('should respond with Unauthorized if access token is not present', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.delete('/SASjsApi/group/123/123')
|
.delete('/SASjsApi/group/123/123')
|
||||||
|
|||||||
@@ -4,12 +4,7 @@ import { MongoMemoryServer } from 'mongodb-memory-server'
|
|||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import appPromise from '../../../app'
|
import appPromise from '../../../app'
|
||||||
import { UserController, GroupController } from '../../../controllers/'
|
import { UserController, GroupController } from '../../../controllers/'
|
||||||
import {
|
import { generateAccessToken, saveTokensInDB } from '../../../utils'
|
||||||
generateAccessToken,
|
|
||||||
saveTokensInDB,
|
|
||||||
AuthProviderType
|
|
||||||
} from '../../../utils'
|
|
||||||
import User from '../../../model/User'
|
|
||||||
|
|
||||||
const clientId = 'someclientID'
|
const clientId = 'someclientID'
|
||||||
const adminUser = {
|
const adminUser = {
|
||||||
@@ -115,16 +110,16 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Conflict if username is already present', async () => {
|
it('should respond with Forbidden if username is already present', async () => {
|
||||||
await controller.createUser(user)
|
await controller.createUser(user)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/user')
|
.post('/SASjsApi/user')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send(user)
|
.send(user)
|
||||||
.expect(409)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('Username already exists.')
|
expect(res.text).toEqual('Error: Username already exists.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -231,36 +226,6 @@ describe('user', () => {
|
|||||||
.expect(400)
|
.expect(400)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Method Not Allowed, when updating username of user created by an external auth provider', async () => {
|
|
||||||
const dbUser = await User.create({
|
|
||||||
...user,
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
const accessToken = await generateAndSaveToken(dbUser!.id)
|
|
||||||
const newUsername = 'newUsername'
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.patch(`/SASjsApi/user/${dbUser!.id}`)
|
|
||||||
.auth(accessToken, { type: 'bearer' })
|
|
||||||
.send({ username: newUsername })
|
|
||||||
.expect(405)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should respond with Method Not Allowed, when updating displayName of user created by an external auth provider', async () => {
|
|
||||||
const dbUser = await User.create({
|
|
||||||
...user,
|
|
||||||
authProvider: AuthProviderType.LDAP
|
|
||||||
})
|
|
||||||
const accessToken = await generateAndSaveToken(dbUser!.id)
|
|
||||||
const newDisplayName = 'My new display Name'
|
|
||||||
|
|
||||||
await request(app)
|
|
||||||
.patch(`/SASjsApi/user/${dbUser!.id}`)
|
|
||||||
.auth(accessToken, { type: 'bearer' })
|
|
||||||
.send({ displayName: newDisplayName })
|
|
||||||
.expect(405)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should respond with Unauthorized if access token is not present', async () => {
|
it('should respond with Unauthorized if access token is not present', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.patch('/SASjsApi/user/1234')
|
.patch('/SASjsApi/user/1234')
|
||||||
@@ -289,7 +254,7 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Conflict if username is already present', async () => {
|
it('should respond with Forbidden if username is already present', async () => {
|
||||||
const dbUser1 = await controller.createUser(user)
|
const dbUser1 = await controller.createUser(user)
|
||||||
const dbUser2 = await controller.createUser({
|
const dbUser2 = await controller.createUser({
|
||||||
...user,
|
...user,
|
||||||
@@ -300,9 +265,9 @@ describe('user', () => {
|
|||||||
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
.patch(`/SASjsApi/user/${dbUser1.id}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ username: dbUser2.username })
|
.send({ username: dbUser2.username })
|
||||||
.expect(409)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('Username already exists.')
|
expect(res.text).toEqual('Error: Username already exists.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -384,7 +349,7 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Conflict if username is already present', async () => {
|
it('should respond with Forbidden if username is already present', async () => {
|
||||||
const dbUser1 = await controller.createUser(user)
|
const dbUser1 = await controller.createUser(user)
|
||||||
const dbUser2 = await controller.createUser({
|
const dbUser2 = await controller.createUser({
|
||||||
...user,
|
...user,
|
||||||
@@ -395,9 +360,9 @@ describe('user', () => {
|
|||||||
.patch(`/SASjsApi/user/by/username/${dbUser1.username}`)
|
.patch(`/SASjsApi/user/by/username/${dbUser1.username}`)
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send({ username: dbUser2.username })
|
.send({ username: dbUser2.username })
|
||||||
.expect(409)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('Username already exists.')
|
expect(res.text).toEqual('Error: Username already exists.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -481,7 +446,7 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Unauthorized when user himself requests and password is incorrect', async () => {
|
it('should respond with Forbidden when user himself requests and password is incorrect', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.id)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
@@ -489,9 +454,9 @@ describe('user', () => {
|
|||||||
.delete(`/SASjsApi/user/${dbUser.id}`)
|
.delete(`/SASjsApi/user/${dbUser.id}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ password: 'incorrectpassword' })
|
.send({ password: 'incorrectpassword' })
|
||||||
.expect(401)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('Invalid password.')
|
expect(res.text).toEqual('Error: Invalid password.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -563,7 +528,7 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Unauthorized when user himself requests and password is incorrect', async () => {
|
it('should respond with Forbidden when user himself requests and password is incorrect', async () => {
|
||||||
const dbUser = await controller.createUser(user)
|
const dbUser = await controller.createUser(user)
|
||||||
const accessToken = await generateAndSaveToken(dbUser.id)
|
const accessToken = await generateAndSaveToken(dbUser.id)
|
||||||
|
|
||||||
@@ -571,9 +536,9 @@ describe('user', () => {
|
|||||||
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
.delete(`/SASjsApi/user/by/username/${dbUser.username}`)
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ password: 'incorrectpassword' })
|
.send({ password: 'incorrectpassword' })
|
||||||
.expect(401)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('Invalid password.')
|
expect(res.text).toEqual('Error: Invalid password.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -687,16 +652,16 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Not Found if userId is incorrect', async () => {
|
it('should respond with Forbidden if userId is incorrect', async () => {
|
||||||
await controller.createUser(user)
|
await controller.createUser(user)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get('/SASjsApi/user/1234')
|
.get('/SASjsApi/user/1234')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('User is not found.')
|
expect(res.text).toEqual('Error: User is not found.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -766,16 +731,16 @@ describe('user', () => {
|
|||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with Not Found if username is incorrect', async () => {
|
it('should respond with Forbidden if username is incorrect', async () => {
|
||||||
await controller.createUser(user)
|
await controller.createUser(user)
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.get('/SASjsApi/user/by/username/randomUsername')
|
.get('/SASjsApi/user/by/username/randomUsername')
|
||||||
.auth(adminAccessToken, { type: 'bearer' })
|
.auth(adminAccessToken, { type: 'bearer' })
|
||||||
.send()
|
.send()
|
||||||
.expect(404)
|
.expect(403)
|
||||||
|
|
||||||
expect(res.text).toEqual('User is not found.')
|
expect(res.text).toEqual('Error: User is not found.')
|
||||||
expect(res.body).toEqual({})
|
expect(res.body).toEqual({})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ userRouter.post('/', authenticateAccessToken, verifyAdmin, async (req, res) => {
|
|||||||
const response = await controller.createUser(body)
|
const response = await controller.createUser(body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ userRouter.get('/', authenticateAccessToken, async (req, res) => {
|
|||||||
const response = await controller.getAllUsers()
|
const response = await controller.getAllUsers()
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ userRouter.get(
|
|||||||
const response = await controller.getUserByUsername(req, username)
|
const response = await controller.getUserByUsername(req, username)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -64,7 +64,7 @@ userRouter.get('/:userId', authenticateAccessToken, async (req, res) => {
|
|||||||
const response = await controller.getUser(req, parseInt(userId))
|
const response = await controller.getUser(req, parseInt(userId))
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ userRouter.patch(
|
|||||||
const response = await controller.updateUserByUsername(username, body)
|
const response = await controller.updateUserByUsername(username, body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -113,7 +113,7 @@ userRouter.patch(
|
|||||||
const response = await controller.updateUser(parseInt(userId), body)
|
const response = await controller.updateUser(parseInt(userId), body)
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -141,7 +141,7 @@ userRouter.delete(
|
|||||||
await controller.deleteUserByUsername(username, data, user!.isAdmin)
|
await controller.deleteUserByUsername(username, data, user!.isAdmin)
|
||||||
res.status(200).send('Account Deleted!')
|
res.status(200).send('Account Deleted!')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -163,7 +163,7 @@ userRouter.delete(
|
|||||||
await controller.deleteUser(parseInt(userId), data, user!.isAdmin)
|
await controller.deleteUser(parseInt(userId), data, user!.isAdmin)
|
||||||
res.status(200).send('Account Deleted!')
|
res.status(200).send('Account Deleted!')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(err.code).send(err.message)
|
res.status(403).send(err.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
1
api/src/types/system/process.d.ts
vendored
1
api/src/types/system/process.d.ts
vendored
@@ -12,5 +12,6 @@ declare namespace NodeJS {
|
|||||||
logger: import('@sasjs/utils/logger').Logger
|
logger: import('@sasjs/utils/logger').Logger
|
||||||
runTimes: import('../../utils').RunTimeType[]
|
runTimes: import('../../utils').RunTimeType[]
|
||||||
secrets: import('../../model/Configuration').ConfigurationType
|
secrets: import('../../model/Configuration').ConfigurationType
|
||||||
|
allowedDomains: string[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export * from './getTokensFromDB'
|
|||||||
export * from './instantiateLogger'
|
export * from './instantiateLogger'
|
||||||
export * from './isDebugOn'
|
export * from './isDebugOn'
|
||||||
export * from './isPublicRoute'
|
export * from './isPublicRoute'
|
||||||
export * from './ldapClient'
|
|
||||||
export * from './zipped'
|
export * from './zipped'
|
||||||
export * from './parseLogToArray'
|
export * from './parseLogToArray'
|
||||||
export * from './removeTokensInDB'
|
export * from './removeTokensInDB'
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
import { createClient, Client } from 'ldapjs'
|
|
||||||
import { ReturnCode } from './verifyEnvVariables'
|
|
||||||
|
|
||||||
export interface LDAPUser {
|
|
||||||
uid: string
|
|
||||||
username: string
|
|
||||||
displayName: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LDAPGroup {
|
|
||||||
name: string
|
|
||||||
members: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LDAPClient {
|
|
||||||
private ldapClient: Client
|
|
||||||
private static classInstance: LDAPClient | null
|
|
||||||
|
|
||||||
private constructor() {
|
|
||||||
process.logger.info('creating LDAP client')
|
|
||||||
this.ldapClient = createClient({ url: process.env.LDAP_URL as string })
|
|
||||||
|
|
||||||
this.ldapClient.on('error', (error) => {
|
|
||||||
process.logger.error(error.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
static async init() {
|
|
||||||
if (!LDAPClient.classInstance) {
|
|
||||||
LDAPClient.classInstance = new LDAPClient()
|
|
||||||
|
|
||||||
process.logger.info('binding LDAP client')
|
|
||||||
await LDAPClient.classInstance.bind().catch((error) => {
|
|
||||||
LDAPClient.classInstance = null
|
|
||||||
throw error
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return LDAPClient.classInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
private async bind() {
|
|
||||||
const promise = new Promise<void>((resolve, reject) => {
|
|
||||||
const { LDAP_BIND_DN, LDAP_BIND_PASSWORD } = process.env
|
|
||||||
this.ldapClient.bind(LDAP_BIND_DN!, LDAP_BIND_PASSWORD!, (error) => {
|
|
||||||
if (error) reject(error)
|
|
||||||
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await promise.catch((error) => {
|
|
||||||
throw new Error(error.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAllLDAPUsers() {
|
|
||||||
const promise = new Promise<LDAPUser[]>((resolve, reject) => {
|
|
||||||
const { LDAP_USERS_BASE_DN } = process.env
|
|
||||||
const filter = `(objectClass=*)`
|
|
||||||
|
|
||||||
this.ldapClient.search(
|
|
||||||
LDAP_USERS_BASE_DN!,
|
|
||||||
{ filter },
|
|
||||||
(error, result) => {
|
|
||||||
if (error) reject(error)
|
|
||||||
|
|
||||||
const users: LDAPUser[] = []
|
|
||||||
|
|
||||||
result.on('searchEntry', (entry) => {
|
|
||||||
users.push({
|
|
||||||
uid: entry.object.uid as string,
|
|
||||||
username: entry.object.username as string,
|
|
||||||
displayName: entry.object.displayname as string
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
result.on('end', (result) => {
|
|
||||||
resolve(users)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return await promise
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(error.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAllLDAPGroups() {
|
|
||||||
const promise = new Promise<LDAPGroup[]>((resolve, reject) => {
|
|
||||||
const { LDAP_GROUPS_BASE_DN } = process.env
|
|
||||||
|
|
||||||
this.ldapClient.search(LDAP_GROUPS_BASE_DN!, {}, (error, result) => {
|
|
||||||
if (error) reject(error)
|
|
||||||
|
|
||||||
const groups: LDAPGroup[] = []
|
|
||||||
|
|
||||||
result.on('searchEntry', (entry) => {
|
|
||||||
const members =
|
|
||||||
typeof entry.object.memberuid === 'string'
|
|
||||||
? [entry.object.memberuid]
|
|
||||||
: entry.object.memberuid
|
|
||||||
groups.push({
|
|
||||||
name: entry.object.cn as string,
|
|
||||||
members
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
result.on('end', (result) => {
|
|
||||||
resolve(groups)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return await promise
|
|
||||||
.then((res) => res)
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error(error.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyUser(username: string, password: string) {
|
|
||||||
const promise = new Promise<boolean>((resolve, reject) => {
|
|
||||||
const { LDAP_USERS_BASE_DN } = process.env
|
|
||||||
const filter = `(username=${username})`
|
|
||||||
|
|
||||||
this.ldapClient.search(
|
|
||||||
LDAP_USERS_BASE_DN!,
|
|
||||||
{ filter },
|
|
||||||
(error, result) => {
|
|
||||||
if (error) reject(error)
|
|
||||||
|
|
||||||
const items: any = []
|
|
||||||
|
|
||||||
result.on('searchEntry', (entry) => {
|
|
||||||
items.push(entry.object)
|
|
||||||
})
|
|
||||||
|
|
||||||
result.on('end', (result) => {
|
|
||||||
if (result?.status !== 0 || items.length === 0) return reject()
|
|
||||||
|
|
||||||
// pick the first found
|
|
||||||
const user = items[0]
|
|
||||||
|
|
||||||
this.ldapClient.bind(user.dn, password, (error) => {
|
|
||||||
if (error) return reject(error)
|
|
||||||
|
|
||||||
resolve(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return await promise
|
|
||||||
.then(() => true)
|
|
||||||
.catch(() => {
|
|
||||||
throw new Error('Invalid password.')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,11 +8,6 @@ export enum ModeType {
|
|||||||
Desktop = 'desktop'
|
Desktop = 'desktop'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AuthProviderType {
|
|
||||||
LDAP = 'ldap',
|
|
||||||
Internal = 'internal'
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ProtocolType {
|
export enum ProtocolType {
|
||||||
HTTP = 'http',
|
HTTP = 'http',
|
||||||
HTTPS = 'https'
|
HTTPS = 'https'
|
||||||
@@ -69,8 +64,6 @@ export const verifyEnvVariables = (): ReturnCode => {
|
|||||||
|
|
||||||
errors.push(...verifyExecutablePaths())
|
errors.push(...verifyExecutablePaths())
|
||||||
|
|
||||||
errors.push(...verifyLDAPVariables())
|
|
||||||
|
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
process.logger?.error(
|
process.logger?.error(
|
||||||
`Invalid environment variable(s) provided: \n${errors.join('\n')}`
|
`Invalid environment variable(s) provided: \n${errors.join('\n')}`
|
||||||
@@ -111,24 +104,13 @@ const verifyMODE = (): string[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.MODE === ModeType.Server) {
|
if (process.env.MODE === ModeType.Server) {
|
||||||
const { DB_CONNECT, AUTH_MECHANISM } = process.env
|
const { DB_CONNECT } = process.env
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'test') {
|
if (process.env.NODE_ENV !== 'test')
|
||||||
if (!DB_CONNECT)
|
if (!DB_CONNECT)
|
||||||
errors.push(
|
errors.push(
|
||||||
`- DB_CONNECT is required for PROTOCOL '${ModeType.Server}'`
|
`- DB_CONNECT is required for PROTOCOL '${ModeType.Server}'`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (AUTH_MECHANISM) {
|
|
||||||
const authMechanismTypes = Object.values(AuthProviderType)
|
|
||||||
if (!authMechanismTypes.includes(AUTH_MECHANISM as AuthProviderType))
|
|
||||||
errors.push(
|
|
||||||
`- AUTH_MECHANISM '${AUTH_MECHANISM}'\n - valid options ${authMechanismTypes}`
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
process.env.AUTH_MECHANISM = DEFAULTS.AUTH_MECHANISM
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
@@ -270,7 +252,7 @@ const verifyRUN_TIMES = (): string[] => {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyExecutablePaths = () => {
|
const verifyExecutablePaths = (): string[] => {
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
const { RUN_TIMES, SAS_PATH, NODE_PATH, PYTHON_PATH, R_PATH, MODE } =
|
const { RUN_TIMES, SAS_PATH, NODE_PATH, PYTHON_PATH, R_PATH, MODE } =
|
||||||
process.env
|
process.env
|
||||||
@@ -298,56 +280,8 @@ const verifyExecutablePaths = () => {
|
|||||||
return errors
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyLDAPVariables = () => {
|
|
||||||
const errors: string[] = []
|
|
||||||
const {
|
|
||||||
LDAP_URL,
|
|
||||||
LDAP_BIND_DN,
|
|
||||||
LDAP_BIND_PASSWORD,
|
|
||||||
LDAP_USERS_BASE_DN,
|
|
||||||
LDAP_GROUPS_BASE_DN,
|
|
||||||
MODE,
|
|
||||||
AUTH_MECHANISM
|
|
||||||
} = process.env
|
|
||||||
|
|
||||||
if (MODE === ModeType.Server && AUTH_MECHANISM === AuthProviderType.LDAP) {
|
|
||||||
if (!LDAP_URL) {
|
|
||||||
errors.push(
|
|
||||||
`- LDAP_URL is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LDAP_BIND_DN) {
|
|
||||||
errors.push(
|
|
||||||
`- LDAP_BIND_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LDAP_BIND_PASSWORD) {
|
|
||||||
errors.push(
|
|
||||||
`- LDAP_BIND_PASSWORD is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LDAP_USERS_BASE_DN) {
|
|
||||||
errors.push(
|
|
||||||
`- LDAP_USERS_BASE_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LDAP_GROUPS_BASE_DN) {
|
|
||||||
errors.push(
|
|
||||||
`- LDAP_GROUPS_BASE_DN is required for AUTH_MECHANISM '${AuthProviderType.LDAP}'`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
MODE: ModeType.Desktop,
|
MODE: ModeType.Desktop,
|
||||||
AUTH_MECHANISM: AuthProviderType.Internal,
|
|
||||||
PROTOCOL: ProtocolType.HTTP,
|
PROTOCOL: ProtocolType.HTTP,
|
||||||
PORT: '5000',
|
PORT: '5000',
|
||||||
HELMET_COEP: HelmetCoepType.TRUE,
|
HELMET_COEP: HelmetCoepType.TRUE,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "ES6",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"rootDir": "./",
|
"rootDir": "./",
|
||||||
"outDir": "./build",
|
"outDir": "./build",
|
||||||
|
|||||||
@@ -15,10 +15,6 @@
|
|||||||
"name": "Auth",
|
"name": "Auth",
|
||||||
"description": "Operations about auth"
|
"description": "Operations about auth"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Auth_Config",
|
|
||||||
"description": "Operations about external auth providers"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Client",
|
"name": "Client",
|
||||||
"description": "Operations about clients"
|
"description": "Operations about clients"
|
||||||
|
|||||||
8081
web/package-lock.json
generated
8081
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server --config webpack.dev.ts --hot",
|
"start": "vite serve --port 3000",
|
||||||
"build": "webpack --config webpack.prod.ts"
|
"build": "vite build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
@@ -30,38 +30,21 @@
|
|||||||
"react-toastify": "^9.0.1"
|
"react-toastify": "^9.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
|
||||||
"@babel/node": "^7.16.0",
|
|
||||||
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
|
||||||
"@babel/preset-env": "^7.16.4",
|
|
||||||
"@babel/preset-react": "^7.16.0",
|
|
||||||
"@babel/preset-typescript": "^7.16.0",
|
|
||||||
"@types/dotenv-webpack": "^7.0.3",
|
|
||||||
"@types/prismjs": "^1.16.6",
|
|
||||||
"@types/react": "^17.0.37",
|
"@types/react": "^17.0.37",
|
||||||
"@types/react-copy-to-clipboard": "^5.0.2",
|
"@types/react-copy-to-clipboard": "^5.0.2",
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-router-dom": "^5.3.1",
|
"@types/react-router-dom": "^5.3.1",
|
||||||
"babel-loader": "^8.2.3",
|
"@vitejs/plugin-react": "^2.1.0",
|
||||||
"babel-plugin-prismjs": "^2.1.0",
|
|
||||||
"copy-webpack-plugin": "^10.0.0",
|
|
||||||
"css-loader": "^6.5.1",
|
|
||||||
"dotenv-webpack": "^7.1.0",
|
|
||||||
"eslint": "^8.5.0",
|
"eslint": "^8.5.0",
|
||||||
"eslint-config-react-app": "^7.0.0",
|
"eslint-config-react-app": "^7.0.0",
|
||||||
"eslint-webpack-plugin": "^3.1.1",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"html-webpack-plugin": "5.5.0",
|
|
||||||
"path": "0.12.7",
|
"path": "0.12.7",
|
||||||
"prettier": "^2.4.1",
|
"prettier": "^2.4.1",
|
||||||
"sass": "^1.44.0",
|
"sass": "^1.44.0",
|
||||||
"sass-loader": "^12.3.0",
|
|
||||||
"style-loader": "^3.3.1",
|
|
||||||
"ts-loader": "^9.2.6",
|
|
||||||
"typescript": "^4.5.2",
|
"typescript": "^4.5.2",
|
||||||
"webpack": "5.64.3",
|
"vite": "^3.1.4",
|
||||||
"webpack-cli": "^4.9.2",
|
"vite-plugin-env-compatible": "^1.1.1",
|
||||||
"webpack-dev-server": "4.7.4"
|
"vite-plugin-html": "^3.2.0",
|
||||||
|
"vite-plugin-monaco-editor": "^1.1.0"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
import axios from 'axios'
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Grid,
|
|
||||||
CircularProgress,
|
|
||||||
Card,
|
|
||||||
CardHeader,
|
|
||||||
Divider,
|
|
||||||
CardContent,
|
|
||||||
TextField,
|
|
||||||
CardActions,
|
|
||||||
Button,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
|
|
||||||
const AuthConfig = () => {
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
|
||||||
const [authDetail, setAuthDetail] = useState<any>({})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoading(true)
|
|
||||||
axios
|
|
||||||
.get(`/SASjsApi/authConfig`)
|
|
||||||
.then((res: any) => {
|
|
||||||
setAuthDetail(res.data)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error('Failed: ' + err.response?.data || err.text, {
|
|
||||||
theme: 'dark',
|
|
||||||
position: toast.POSITION.BOTTOM_RIGHT
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.finally(() => setIsLoading(false))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const synchroniseWithLDAP = () => {
|
|
||||||
setIsLoading(true)
|
|
||||||
axios
|
|
||||||
.post(`/SASjsApi/authConfig/synchroniseWithLDAP`)
|
|
||||||
.then((res: any) => {
|
|
||||||
const { userCount, groupCount } = res.data
|
|
||||||
toast.success(
|
|
||||||
`Imported ${userCount} ${
|
|
||||||
userCount > 1 ? 'users' : 'user'
|
|
||||||
} and ${groupCount} ${groupCount > 1 ? 'groups' : 'group'}`,
|
|
||||||
{
|
|
||||||
theme: 'dark',
|
|
||||||
position: toast.POSITION.BOTTOM_RIGHT
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error('Failed: ' + err.response?.data || err.text, {
|
|
||||||
theme: 'dark',
|
|
||||||
position: toast.POSITION.BOTTOM_RIGHT
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.finally(() => setIsLoading(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
return isLoading ? (
|
|
||||||
<CircularProgress
|
|
||||||
style={{ position: 'absolute', left: '50%', top: '50%' }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Box>
|
|
||||||
{Object.entries(authDetail).length === 0 && (
|
|
||||||
<Typography>No external Auth Provider is used</Typography>
|
|
||||||
)}
|
|
||||||
{authDetail.ldap && (
|
|
||||||
<Card>
|
|
||||||
<CardHeader title="LDAP Authentication" />
|
|
||||||
<Divider />
|
|
||||||
<CardContent>
|
|
||||||
<Grid container spacing={4}>
|
|
||||||
<Grid item md={6} xs={12}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
label="LDAP_URL"
|
|
||||||
name="LDAP_URL"
|
|
||||||
value={authDetail.ldap.LDAP_URL}
|
|
||||||
variant="outlined"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item md={6} xs={12}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
label="LDAP_BIND_DN"
|
|
||||||
name="LDAP_BIND_DN"
|
|
||||||
value={authDetail.ldap.LDAP_BIND_DN}
|
|
||||||
variant="outlined"
|
|
||||||
disabled={true}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item md={6} xs={12}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
label="LDAP_BIND_PASSWORD"
|
|
||||||
name="LDAP_BIND_PASSWORD"
|
|
||||||
type="password"
|
|
||||||
value={authDetail.ldap.LDAP_BIND_PASSWORD}
|
|
||||||
variant="outlined"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item md={6} xs={12}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
label="LDAP_USERS_BASE_DN"
|
|
||||||
name="LDAP_USERS_BASE_DN"
|
|
||||||
value={authDetail.ldap.LDAP_USERS_BASE_DN}
|
|
||||||
variant="outlined"
|
|
||||||
disabled={true}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item md={6} xs={12}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
label="LDAP_GROUPS_BASE_DN"
|
|
||||||
name="LDAP_GROUPS_BASE_DN"
|
|
||||||
value={authDetail.ldap.LDAP_GROUPS_BASE_DN}
|
|
||||||
variant="outlined"
|
|
||||||
disabled={true}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
<Divider />
|
|
||||||
<CardActions>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
onClick={synchroniseWithLDAP}
|
|
||||||
>
|
|
||||||
Synchronise
|
|
||||||
</Button>
|
|
||||||
</CardActions>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AuthConfig
|
|
||||||
@@ -7,7 +7,6 @@ import TabPanel from '@mui/lab/TabPanel'
|
|||||||
|
|
||||||
import Permission from './permission'
|
import Permission from './permission'
|
||||||
import Profile from './profile'
|
import Profile from './profile'
|
||||||
import AuthConfig from './authConfig'
|
|
||||||
|
|
||||||
import { AppContext, ModeType } from '../../context/appContext'
|
import { AppContext, ModeType } from '../../context/appContext'
|
||||||
import PermissionsContextProvider from '../../context/permissionsContext'
|
import PermissionsContextProvider from '../../context/permissionsContext'
|
||||||
@@ -60,9 +59,6 @@ const Settings = () => {
|
|||||||
{appContext.mode === ModeType.Server && (
|
{appContext.mode === ModeType.Server && (
|
||||||
<StyledTab label="Permissions" value="permission" />
|
<StyledTab label="Permissions" value="permission" />
|
||||||
)}
|
)}
|
||||||
{appContext.mode === ModeType.Server && appContext.isAdmin && (
|
|
||||||
<StyledTab label="Auth Config" value="auth_config" />
|
|
||||||
)}
|
|
||||||
</TabList>
|
</TabList>
|
||||||
</Box>
|
</Box>
|
||||||
<StyledTabpanel value="profile">
|
<StyledTabpanel value="profile">
|
||||||
@@ -73,9 +69,6 @@ const Settings = () => {
|
|||||||
<Permission />
|
<Permission />
|
||||||
</PermissionsContextProvider>
|
</PermissionsContextProvider>
|
||||||
</StyledTabpanel>
|
</StyledTabpanel>
|
||||||
<StyledTabpanel value="auth_config">
|
|
||||||
<AuthConfig />
|
|
||||||
</StyledTabpanel>
|
|
||||||
</TabContext>
|
</TabContext>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,5 +35,6 @@
|
|||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
-->
|
-->
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
17
web/vite.config.js
Normal file
17
web/vite.config.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import { createHtmlPlugin } from 'vite-plugin-html'
|
||||||
|
import envCompatible from 'vite-plugin-env-compatible'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
outDir: './build'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
createHtmlPlugin({
|
||||||
|
template: './src/index.html'
|
||||||
|
}),
|
||||||
|
envCompatible({ prefix: '' })
|
||||||
|
]
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user