[389-ds-base] branch 389-ds-base-1.3.10 updated: Ticket 50510 - etime can contain invalid nanosecond value
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.10
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.10 by this push:
new bae369e Ticket 50510 - etime can contain invalid nanosecond value
bae369e is described below
commit bae369e6d8b26660d9da9e1e01e955deb499a55d
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Tue Jul 23 13:59:01 2019 +0200
Ticket 50510 - etime can contain invalid nanosecond value
Bug Description:
When computing the etime, it takes into account the nanosecond.
At border of a second, the ending nsec can be lower than starting nsec.
In such case the computation is wrong as delta=(ending_nsec - starting_nsec) is negative.
final_nsec = 1 - delta > 1sec
Fix Description:
if delta=(ending_nsec - starting_nsec) is negative
final_nsec = 1 + delta < 1sec
https://pagure.io/389-ds-base/issue/50510
Reviewed by: Mark Reynolds (Thanks!)
Platforms tested: F28
Flag Day: no
Doc impact: no
---
dirsrvtests/tests/suites/ds_logs/ds_logs_test.py | 44 ++++++++++++++++++++++++
ldap/servers/slapd/time.c | 6 ++--
2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py b/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py
index fb73a22..6f1e93c 100644
--- a/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py
+++ b/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py
@@ -186,7 +186,51 @@ def test_log_plugin_off(topology_st):
assert len(access_log_lines) > 0
assert not topology_st.standalone.ds_access_log.match('^\[.+\d{9}.+\].+')
+(a)pytest.mark.bz1732053
+(a)pytest.mark.ds50510
+def test_etime_at_border_of_second(topology_st):
+ topo = topology_st.standalone
+ # be sure to analyze only the following rapid OPs
+ topo.stop()
+ os.remove(topo.accesslog)
+ topo.start()
+
+ prog = os.path.join(topo.ds_paths.bin_dir, 'rsearch')
+
+ cmd = [prog]
+
+ # base search
+ cmd.extend(['-s', DN_CONFIG])
+
+ # scope of the search
+ cmd.extend(['-S', '0'])
+
+ # host / port
+ cmd.extend(['-h', HOST_STANDALONE])
+ cmd.extend(['-p', str(PORT_STANDALONE)])
+
+ # bound as DM to make it faster
+ cmd.extend(['-D', DN_DM])
+ cmd.extend(['-w', PASSWORD])
+
+ # filter
+ cmd.extend(['-f', "(cn=config)"])
+
+ # 2 samples SRCH
+ cmd.extend(['-C', "2"])
+
+ output = subprocess.check_output(cmd)
+ topo.stop()
+
+ # No etime with 0.199xxx (everything should be few ms)
+ invalid_etime = topo.ds_access_log.match(r'.*etime=0\.19.*')
+ if invalid_etime:
+ for i in range(len(invalid_etime)):
+ log.error('It remains invalid or weird etime: %s' % invalid_etime[i])
+ assert not invalid_etime
+
+
if __name__ == '__main__':
# Run isolated
# -s for DEBUG mode
diff --git a/ldap/servers/slapd/time.c b/ldap/servers/slapd/time.c
index 584bd1e..8048a33 100644
--- a/ldap/servers/slapd/time.c
+++ b/ldap/servers/slapd/time.c
@@ -235,8 +235,10 @@ slapi_timespec_diff(struct timespec *a, struct timespec *b, struct timespec *dif
if (nsec < 0) {
/* It's negative so take one second */
sec -= 1;
- /* And set nsec to to a whole value */
- nsec = 1000000000 - nsec;
+ /* And set nsec to to a whole value
+ * nsec is negative => nsec = 1s - abs(nsec)
+ */
+ nsec = 1000000000 + nsec;
}
diff->tv_sec = sec;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] 01/01: Bump version to 1.3.10
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.10
in repository 389-ds-base.
commit 3204c964657e8756d6740c89597f65de0933230e
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Wed Jul 31 13:44:04 2019 -0400
Bump version to 1.3.10
---
VERSION.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/VERSION.sh b/VERSION.sh
index 44c045b..71bb621 100644
--- a/VERSION.sh
+++ b/VERSION.sh
@@ -10,7 +10,7 @@ vendor="389 Project"
# PACKAGE_VERSION is constructed from these
VERSION_MAJOR=1
VERSION_MINOR=3
-VERSION_MAINT=9.1
+VERSION_MAINT=10.1
# NOTE: VERSION_PREREL is automatically set for builds made out of a git tree
VERSION_PREREL=
VERSION_DATE=$(date -u +%Y%m%d)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] branch 389-ds-base-1.4.0 updated: Issue 50508 - UI - fix local password policy form
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new 9a7aab2 Issue 50508 - UI - fix local password policy form
9a7aab2 is described below
commit 9a7aab2511132203e9a509171fa34e02eb825434
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Mon Jul 22 12:51:54 2019 -0400
Issue 50508 - UI - fix local password policy form
Description: The modal width is too narrow and it overflows
relates: https://pagure.io/389-ds-base/issue/50508
Reviewed by: mreynolds(one line commit rule)
---
src/cockpit/389-console/src/css/ds.css | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css
index f8945bf..8082d1e 100644
--- a/src/cockpit/389-console/src/css/ds.css
+++ b/src/cockpit/389-console/src/css/ds.css
@@ -706,9 +706,9 @@ td {
width: 450px;
}
-.ds-modal-wide .modal-content{
- width: 850px !important;
- min-width: 850px !important;
+.ds-modal-wide {
+ width: 875px !important;
+ min-width: 875px !important;
vertical-align: middle;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] branch master updated: Issue 50508 - UI - fix local password policy form
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch master
in repository 389-ds-base.
The following commit(s) were added to refs/heads/master by this push:
new 53efe7a Issue 50508 - UI - fix local password policy form
53efe7a is described below
commit 53efe7a19cfc37d358ca92c5be25ea7f0a3912b3
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Mon Jul 22 12:51:54 2019 -0400
Issue 50508 - UI - fix local password policy form
Description: The modal width is too narrow and it overflows
relates: https://pagure.io/389-ds-base/issue/50508
Reviewed by: mreynolds(one line commit rule)
---
src/cockpit/389-console/src/css/ds.css | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css
index f8945bf..8082d1e 100644
--- a/src/cockpit/389-console/src/css/ds.css
+++ b/src/cockpit/389-console/src/css/ds.css
@@ -706,9 +706,9 @@ td {
width: 450px;
}
-.ds-modal-wide .modal-content{
- width: 850px !important;
- min-width: 850px !important;
+.ds-modal-wide {
+ width: 875px !important;
+ min-width: 875px !important;
vertical-align: middle;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] 02/02: Bump version to 1.4.0.26
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
commit 8a2d3de6f5afa8ffcae0b9a895d2de6495b0f7ec
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Fri Jul 19 11:50:12 2019 -0400
Bump version to 1.4.0.26
---
VERSION.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/VERSION.sh b/VERSION.sh
index 3bebfc8..40304a2 100644
--- a/VERSION.sh
+++ b/VERSION.sh
@@ -10,7 +10,7 @@ vendor="389 Project"
# PACKAGE_VERSION is constructed from these
VERSION_MAJOR=1
VERSION_MINOR=4
-VERSION_MAINT=0.25
+VERSION_MAINT=0.26
# NOTE: VERSION_PREREL is automatically set for builds made out of a git tree
VERSION_PREREL=
VERSION_DATE=$(date -u +%Y%m%d)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] 01/02: Issue 50499 - Fix audit issues and remove jquery from the whitelist
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
commit 7e5e30e393d068a2c50e0651b1e17d4745443170
Author: Simon Pichugin <spichugi(a)redhat.com>
AuthorDate: Mon Jul 15 23:28:45 2019 +0200
Issue 50499 - Fix audit issues and remove jquery from the whitelist
Description: 50 high vulnerabilities were found during audit. Fix them.
It updates the Patternfly version to 3.59.3 version.
Package jquery is no longer an issue, remove it from the whitelist.
https://pagure.io/389-ds-base/issue/50499
Reviewed by: mreynolds (Thanks!)
---
src/cockpit/389-console/audit-ci.json | 3 +-
src/cockpit/389-console/package-lock.json | 149 +++++++++++++++---------------
src/cockpit/389-console/package.json | 2 +-
3 files changed, 76 insertions(+), 78 deletions(-)
diff --git a/src/cockpit/389-console/audit-ci.json b/src/cockpit/389-console/audit-ci.json
index 78b590f..96915fa 100644
--- a/src/cockpit/389-console/audit-ci.json
+++ b/src/cockpit/389-console/audit-ci.json
@@ -3,6 +3,5 @@
"package-manager": "auto",
"report": true,
"advisories": [],
- "_comment": "jquery should be removed from the whitelist after https://github.com/patternfly/patternfly/pull/1174 is merged",
- "whitelist": ["jquery"]
+ "whitelist": []
}
diff --git a/src/cockpit/389-console/package-lock.json b/src/cockpit/389-console/package-lock.json
index 16a54ab..9fec71c 100644
--- a/src/cockpit/389-console/package-lock.json
+++ b/src/cockpit/389-console/package-lock.json
@@ -1044,7 +1044,8 @@
"@types/d3-color": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.2.2.tgz",
- "integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw=="
+ "integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw==",
+ "optional": true
},
"@types/d3-dispatch": {
"version": "1.0.7",
@@ -1064,7 +1065,8 @@
"@types/d3-dsv": {
"version": "1.0.36",
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.36.tgz",
- "integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA=="
+ "integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA==",
+ "optional": true
},
"@types/d3-ease": {
"version": "1.0.8",
@@ -1103,6 +1105,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz",
"integrity": "sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ==",
+ "optional": true,
"requires": {
"@types/d3-color": "*"
}
@@ -1110,7 +1113,8 @@
"@types/d3-path": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz",
- "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA=="
+ "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==",
+ "optional": true
},
"@types/d3-polygon": {
"version": "1.0.7",
@@ -1157,7 +1161,8 @@
"@types/d3-selection": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.1.tgz",
- "integrity": "sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA=="
+ "integrity": "sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA==",
+ "optional": true
},
"@types/d3-shape": {
"version": "1.3.1",
@@ -1171,7 +1176,8 @@
"@types/d3-time": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.10.tgz",
- "integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw=="
+ "integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw==",
+ "optional": true
},
"@types/d3-time-format": {
"version": "2.1.1",
@@ -2112,9 +2118,9 @@
"integrity": "sha512-CB9CrpNVrIytlOoqHtRXhhxFo/jencr1U5cMqPBA0WmMdb13bzjHnXQVNGYde/g5gWW+RWiuT9jTquZuz3VE8A=="
},
"bootstrap-switch": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/bootstrap-switch/-/bootstrap-switch-3.3.5.tgz",
- "integrity": "sha512-aRwgTPO7QPvTtUxit2ucXgs/P+dp3Y8Qy41XOOqTXZiJvfI6b87+hP+r4B4+3y7bptu0P6KHIyEc4ordEVIVkg==",
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/bootstrap-switch/-/bootstrap-switch-3.3.4.tgz",
+ "integrity": "sha1-cOCusqh3wNx2aZHeEI4hcPwpov8=",
"optional": true
},
"bootstrap-touchspin": {
@@ -2896,17 +2902,18 @@
"version": "1.10.19",
"resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.19.tgz",
"integrity": "sha512-+ljXcI6Pj3PTGy5pesp3E5Dr3x3AV45EZe0o1r0gKENN2gafBKXodVnk2ypKwl2tTmivjxbkiqoWnipTefyBTA==",
+ "optional": true,
"requires": {
"jquery": ">=1.7"
}
},
"datatables.net-bs": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/datatables.net-bs/-/datatables.net-bs-2.1.1.tgz",
- "integrity": "sha1-cEEIlyiRlJ0JS/RPU9BlTZ/ue84=",
+ "version": "1.10.19",
+ "resolved": "https://registry.npmjs.org/datatables.net-bs/-/datatables.net-bs-1.10.19.tgz",
+ "integrity": "sha512-5gxoI2n+duZP06+4xVC2TtH6zcY369/TRKTZ1DdSgDcDUl4OYQsrXCuaLJmbVzna/5Y5lrMmK7CxgvYgIynICA==",
"optional": true,
"requires": {
- "datatables.net": ">=1.10.9",
+ "datatables.net": "1.10.19",
"jquery": ">=1.7"
}
},
@@ -4216,7 +4223,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"aproba": {
"version": "1.2.0",
@@ -4237,12 +4245,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -4257,17 +4267,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -4384,7 +4397,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"ini": {
"version": "1.3.5",
@@ -4396,6 +4410,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -4410,6 +4425,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -4417,12 +4433,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -4441,6 +4459,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -4528,7 +4547,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -4540,6 +4560,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"wrappy": "1"
}
@@ -4625,7 +4646,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -4661,6 +4683,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -4680,6 +4703,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
+ "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -4723,12 +4747,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
}
}
},
@@ -5792,9 +5818,9 @@
}
},
"lodash": {
- "version": "4.17.11",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
- "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+ "version": "4.17.14",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz",
+ "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw=="
},
"lodash.assign": {
"version": "4.2.0",
@@ -5812,9 +5838,9 @@
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"lodash.mergewith": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
- "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ=="
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
},
"lodash.tail": {
"version": "4.1.1",
@@ -6037,9 +6063,9 @@
}
},
"mixin-deep": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
- "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
"dev": true,
"requires": {
"for-in": "^1.0.2",
@@ -6093,11 +6119,12 @@
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
- "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
+ "optional": true
},
"moment-timezone": {
"version": "0.4.1",
- "resolved": "http://registry.npmjs.org/moment-timezone/-/moment-timezone-0.4.1.tgz",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.4.1.tgz",
"integrity": "sha1-gfWYw61eIs2teWtn7NjYjQ9bqgY=",
"optional": true,
"requires": {
@@ -6611,17 +6638,17 @@
}
},
"patternfly": {
- "version": "3.59.1",
- "resolved": "https://registry.npmjs.org/patternfly/-/patternfly-3.59.1.tgz",
- "integrity": "sha512-0Q/P58yaxcQXwnXo/OssiXaZmuX0g9QvWdpsYHyml4ihqnN2lL/yGdadFarA6UAQb//15XtNjKHZocoJXCkWYg==",
+ "version": "3.59.3",
+ "resolved": "https://registry.npmjs.org/patternfly/-/patternfly-3.59.3.tgz",
+ "integrity": "sha512-gStdjLCS9k6NmI2xCXa1IBK0s8p5l5dqMEh/zLEUwA+qdV6z6qwSxHe8QT3AjLyEy27qMSzmtUXxvkO1c8jENw==",
"requires": {
"@types/c3": "^0.6.0",
- "bootstrap": "~3.4.0",
+ "bootstrap": "~3.4.1",
"bootstrap-datepicker": "^1.7.1",
"bootstrap-sass": "^3.4.0",
"bootstrap-select": "1.12.2",
"bootstrap-slider": "^9.9.0",
- "bootstrap-switch": "~3.3.4",
+ "bootstrap-switch": "3.3.4",
"bootstrap-touchspin": "~3.1.1",
"c3": "~0.4.11",
"d3": "~3.5.17",
@@ -6634,7 +6661,7 @@
"font-awesome": "^4.7.0",
"font-awesome-sass": "^4.7.0",
"google-code-prettify": "~1.0.5",
- "jquery": "~3.2.1",
+ "jquery": "~3.4.1",
"jquery-match-height": "^0.7.2",
"moment": "^2.19.1",
"moment-timezone": "^0.4.1",
@@ -6646,11 +6673,6 @@
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz",
"integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA=="
- },
- "jquery": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz",
- "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c="
}
}
},
@@ -7766,9 +7788,9 @@
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"set-value": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
- "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
@@ -8623,38 +8645,15 @@
"dev": true
},
"union-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
- "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
"dev": true,
"requires": {
"arr-union": "^3.1.0",
"get-value": "^2.0.6",
"is-extendable": "^0.1.1",
- "set-value": "^0.4.3"
- },
- "dependencies": {
- "extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
- "dev": true,
- "requires": {
- "is-extendable": "^0.1.0"
- }
- },
- "set-value": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
- "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
- "dev": true,
- "requires": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.1",
- "to-object-path": "^0.3.0"
- }
- }
+ "set-value": "^2.0.1"
}
},
"uniq": {
diff --git a/src/cockpit/389-console/package.json b/src/cockpit/389-console/package.json
index eb269d9..ddf44b5 100644
--- a/src/cockpit/389-console/package.json
+++ b/src/cockpit/389-console/package.json
@@ -51,7 +51,7 @@
"dependencies": {
"bootstrap": "^4.3.1",
"node-sass": "4.11.0",
- "patternfly": "^3.59.1",
+ "patternfly": "^3.59.3",
"patternfly-react": "^2.34.3",
"prop-types": "15.6.2",
"react": "16.6.1",
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] branch master updated: Bump version to 1.4.1.6
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch master
in repository 389-ds-base.
The following commit(s) were added to refs/heads/master by this push:
new 5ac5a8a Bump version to 1.4.1.6
5ac5a8a is described below
commit 5ac5a8aadd42551ea0389907fd286b7d60157685
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Fri Jul 19 10:44:46 2019 -0400
Bump version to 1.4.1.6
---
VERSION.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/VERSION.sh b/VERSION.sh
index e9e12a2..9a7d5ca 100644
--- a/VERSION.sh
+++ b/VERSION.sh
@@ -10,7 +10,7 @@ vendor="389 Project"
# PACKAGE_VERSION is constructed from these
VERSION_MAJOR=1
VERSION_MINOR=4
-VERSION_MAINT=1.5
+VERSION_MAINT=1.6
# NOTE: VERSION_PREREL is automatically set for builds made out of a git tree
VERSION_PREREL=
VERSION_DATE=$(date -u +%Y%m%d)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] branch 389-ds-base-1.4.0 updated: Issue 50355 - SSL version min and max not correctly applied
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new f46334f Issue 50355 - SSL version min and max not correctly applied
f46334f is described below
commit f46334f25ed91fe3c0427448e46e1ed845b52712
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Thu Jul 18 21:44:07 2019 -0400
Issue 50355 - SSL version min and max not correctly applied
Bug Description: Setting the sslVersionMin or SSLVersionMax was not
correctly applied and the NSS default min and max
became the valid range.
Fix Description: Do not attempt to reset the requested range based off
of hardcoded limits. Also removed obsolete SSL3 code,
and fixed a minor memory leak in main.c found during
ASAN testing.
Relates: https://pagure.io/389-ds-base/issue/50355
ASAN approved
Reviewed by: tbordaz(Thanks!)
---
dirsrvtests/tests/suites/tls/ssl_version_test.py | 55 +++
ldap/servers/slapd/main.c | 5 +-
ldap/servers/slapd/ssl.c | 424 ++++-------------------
src/lib389/lib389/instance/remove.py | 2 +-
4 files changed, 118 insertions(+), 368 deletions(-)
diff --git a/dirsrvtests/tests/suites/tls/ssl_version_test.py b/dirsrvtests/tests/suites/tls/ssl_version_test.py
new file mode 100644
index 0000000..acc8b23
--- /dev/null
+++ b/dirsrvtests/tests/suites/tls/ssl_version_test.py
@@ -0,0 +1,55 @@
+import logging
+import pytest
+import os
+from lib389.config import Encryption
+from lib389.topologies import topology_st as topo
+
+DEBUGGING = os.getenv("DEBUGGING", default=False)
+if DEBUGGING:
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
+else:
+ logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
+
+
+def test_ssl_version_range(topo):
+ """Specify a test case purpose or name here
+
+ :id: bc400f54-3966-49c8-b640-abbf4fb2377e
+ 1. Get current default range
+ 2. Set sslVersionMin and verify it is applied after a restart
+ 3. Set sslVersionMax and verify it is applied after a restart
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ """
+
+ topo.standalone.enable_tls()
+ enc = Encryption(topo.standalone)
+ default_min = enc.get_attr_val_utf8('sslVersionMin')
+ default_max = enc.get_attr_val_utf8('sslVersionMax')
+ log.info(f"default min: {default_min} max: {default_max}")
+ if DEBUGGING:
+ topo.standalone.config.set('nsslapd-auditlog-logging-enabled', 'on')
+
+ # Test that setting the min version is applied after a restart
+ enc.replace('sslVersionMin', default_max)
+ enc.replace('sslVersionMax', default_max)
+ topo.standalone.restart()
+ min = enc.get_attr_val_utf8('sslVersionMin')
+ assert min == default_max
+
+ # Test that setting the max version is applied after a restart
+ enc.replace('sslVersionMin', default_min)
+ enc.replace('sslVersionMax', default_min)
+ topo.standalone.restart()
+ max = enc.get_attr_val_utf8('sslVersionMax')
+ assert max == default_min
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main(["-s", CURRENT_FILE])
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
index 2c7b532..8224cd0 100644
--- a/ldap/servers/slapd/main.c
+++ b/ldap/servers/slapd/main.c
@@ -911,14 +911,13 @@ main(int argc, char **argv)
slapi_ch_free_string(&securelistenhost);
#if defined(ENABLE_LDAPI)
- if (config_get_ldapi_switch() &&
- config_get_ldapi_filename() != 0) {
+ if (config_get_ldapi_switch() && slapdFrontendConfig->ldapi_filename != 0) {
mcfg.i_port = ports_info.i_port = 1; /* flag ldapi as on */
ports_info.i_listenaddr = (PRNetAddr **)slapi_ch_calloc(2, sizeof(PRNetAddr *));
*ports_info.i_listenaddr = (PRNetAddr *)slapi_ch_calloc(1, sizeof(PRNetAddr));
(*ports_info.i_listenaddr)->local.family = PR_AF_LOCAL;
PL_strncpyz((*ports_info.i_listenaddr)->local.path,
- config_get_ldapi_filename(),
+ slapdFrontendConfig->ldapi_filename,
sizeof((*ports_info.i_listenaddr)->local.path));
unlink((*ports_info.i_listenaddr)->local.path);
}
diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c
index a89b1de..37683bc 100644
--- a/ldap/servers/slapd/ssl.c
+++ b/ldap/servers/slapd/ssl.c
@@ -48,8 +48,8 @@
* sslVersionMax: max ssl version supported by NSS
******************************************************************************/
-#define DEFVERSION "TLS1.2"
-#define CURRENT_DEFAULT_SSL_VERSION SSL_LIBRARY_VERSION_TLS_1_2
+#define DEFVERSION "TLS1.0"
+#define CURRENT_DEFAULT_SSL_VERSION SSL_LIBRARY_VERSION_TLS_1_0
extern char *slapd_SSL3ciphers;
extern symbol_t supported_ciphers[];
@@ -137,75 +137,6 @@ typedef struct
static cipherstruct *_conf_ciphers = NULL;
static void _conf_init_ciphers(void);
-/*
- * This lookup table is for supporting the old cipher name.
- * Once swtiching to the NSS cipherSuiteName is done,
- * this lookup_cipher table can be removed.
- */
-typedef struct
-{
- char *alias;
- char *name;
-} lookup_cipher;
-static lookup_cipher _lookup_cipher[] = {
- {"rc4", "SSL_CK_RC4_128_WITH_MD5"},
- {"rc4export", "SSL_CK_RC4_128_EXPORT40_WITH_MD5"},
- {"rc2", "SSL_CK_RC2_128_CBC_WITH_MD5"},
- {"rc2export", "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"},
- /*{"idea", "SSL_EN_IDEA_128_CBC_WITH_MD5"}, */
- {"des", "SSL_CK_DES_64_CBC_WITH_MD5"},
- {"desede3", "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"},
- {"rsa_rc4_128_md5", "TLS_RSA_WITH_RC4_128_MD5"},
- {"rsa_rc4_128_sha", "TLS_RSA_WITH_RC4_128_SHA"},
- {"rsa_3des_sha", "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
- {"tls_rsa_3des_sha", "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
- {"rsa_fips_3des_sha", "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"},
- {"fips_3des_sha", "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"},
- {"rsa_des_sha", "TLS_RSA_WITH_DES_CBC_SHA"},
- {"rsa_fips_des_sha", "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
- {"fips_des_sha", "SSL_RSA_FIPS_WITH_DES_CBC_SHA"}, /* ditto */
- {"rsa_rc4_40_md5", "TLS_RSA_EXPORT_WITH_RC4_40_MD5"},
- {"tls_rsa_rc4_40_md5", "TLS_RSA_EXPORT_WITH_RC4_40_MD5"},
- {"rsa_rc2_40_md5", "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
- {"tls_rsa_rc2_40_md5", "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
- {"rsa_null_md5", "TLS_RSA_WITH_NULL_MD5"}, /* disabled by default */
- {"rsa_null_sha", "TLS_RSA_WITH_NULL_SHA"}, /* disabled by default */
- {"tls_rsa_export1024_with_rc4_56_sha", "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"},
- {"rsa_rc4_56_sha", "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"}, /* ditto */
- {"tls_rsa_export1024_with_des_cbc_sha", "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"},
- {"rsa_des_56_sha", "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA"}, /* ditto */
- {"fortezza", ""}, /* deprecated */
- {"fortezza_rc4_128_sha", ""}, /* deprecated */
- {"fortezza_null", ""}, /* deprecated */
-
- /*{"dhe_dss_40_sha", SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, 0}, */
- {"dhe_dss_des_sha", "TLS_DHE_DSS_WITH_DES_CBC_SHA"},
- {"dhe_dss_3des_sha", "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"},
- {"dhe_rsa_40_sha", "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"},
- {"dhe_rsa_des_sha", "TLS_DHE_RSA_WITH_DES_CBC_SHA"},
- {"dhe_rsa_3des_sha", "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"},
-
- {"tls_rsa_aes_128_sha", "TLS_RSA_WITH_AES_128_CBC_SHA"},
- {"rsa_aes_128_sha", "TLS_RSA_WITH_AES_128_CBC_SHA"}, /* ditto */
- {"tls_dh_dss_aes_128_sha", ""}, /* deprecated */
- {"tls_dh_rsa_aes_128_sha", ""}, /* deprecated */
- {"tls_dhe_dss_aes_128_sha", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"},
- {"tls_dhe_rsa_aes_128_sha", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
-
- {"tls_rsa_aes_256_sha", "TLS_RSA_WITH_AES_256_CBC_SHA"},
- {"rsa_aes_256_sha", "TLS_RSA_WITH_AES_256_CBC_SHA"}, /* ditto */
- {"tls_dss_aes_256_sha", ""}, /* deprecated */
- {"tls_rsa_aes_256_sha", ""}, /* deprecated */
- {"tls_dhe_dss_aes_256_sha", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"},
- {"tls_dhe_rsa_aes_256_sha", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
- /*{"tls_dhe_dss_1024_des_sha", ""}, */
- {"tls_dhe_dss_1024_rc4_sha", "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA"},
- {"tls_dhe_dss_rc4_128_sha", "TLS_DHE_DSS_WITH_RC4_128_SHA"},
- /* New in NSS 3.15 */
- {"tls_rsa_aes_128_gcm_sha", "TLS_RSA_WITH_AES_128_GCM_SHA256"},
- {"tls_dhe_rsa_aes_128_gcm_sha", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"},
- {"tls_dhe_dss_aes_128_gcm_sha", NULL}, /* not available */
- {NULL, NULL}};
/* E.g., "SSL3", "TLS1.2", "Unknown SSL version: 0x0" */
#define VERSION_STR_LENGTH 64
@@ -705,7 +636,6 @@ _conf_setciphers(char *setciphers, int flags)
if (strcasecmp(setciphers, "all")) { /* if not all */
PRBool enabled = active ? PR_TRUE : PR_FALSE;
- int lookup = 1;
for (x = 0; _conf_ciphers[x].name; x++) {
if (!PL_strcasecmp(setciphers, _conf_ciphers[x].name)) {
if (_conf_ciphers[x].flags & CIPHER_IS_WEAK) {
@@ -732,55 +662,10 @@ _conf_setciphers(char *setciphers, int flags)
enabledOne = PR_TRUE; /* At least one active cipher is set. */
}
SSL_CipherPrefSetDefault(_conf_ciphers[x].num, enabled);
- lookup = 0;
break;
}
}
- if (lookup) { /* lookup with old cipher name and get NSS cipherSuiteName */
- for (size_t i = 0; _lookup_cipher[i].alias; i++) {
- if (!PL_strcasecmp(setciphers, _lookup_cipher[i].alias)) {
- if (enabled && !_lookup_cipher[i].name[0]) {
- slapd_SSL_warn("Cipher suite %s is not available in NSS %d.%d. Ignoring %s",
- setciphers, NSS_VMAJOR, NSS_VMINOR, setciphers);
- continue;
- }
- for (x = 0; _conf_ciphers[x].name; x++) {
- if (!PL_strcasecmp(_lookup_cipher[i].name, _conf_ciphers[x].name)) {
- if (enabled) {
- if (_conf_ciphers[x].flags & CIPHER_IS_WEAK) {
- if (active && CIPHER_SET_ALLOWSWEAKCIPHER(flags)) {
- slapd_SSL_warn("Cipher %s is weak. "
- "It is enabled since allowWeakCipher is \"on\" "
- "(default setting for the backward compatibility). "
- "We strongly recommend to set it to \"off\". "
- "Please replace the value of allowWeakCipher with \"off\" in "
- "the encryption config entry cn=encryption,cn=config and "
- "restart the server.",
- setciphers);
- } else {
- /* if the cipher is weak and we don't allow weak cipher,
- disable it. */
- enabled = PR_FALSE;
- }
- }
- if (enabled) {
- /* if the cipher is not weak or we allow weak cipher,
- check fips. */
- enabled = cipher_check_fips(x, NULL, &unsuplist);
- }
- }
- if (enabled) {
- enabledOne = PR_TRUE; /* At least one active cipher is set. */
- }
- SSL_CipherPrefSetDefault(_conf_ciphers[x].num, enabled);
- break;
- }
- }
- break;
- }
- }
- }
- if (!lookup && !_conf_ciphers[x].name) { /* If lookup, it's already reported. */
+ if (!_conf_ciphers[x].name) {
slapd_SSL_warn("Cipher suite %s is not available in NSS %d.%d. Ignoring %s",
setciphers, NSS_VMAJOR, NSS_VMINOR, setciphers);
}
@@ -1029,124 +914,6 @@ slapi_getSSLVersion_str(PRUint16 vnum, char *buf, size_t bufsize)
#define SSLVGreater(x, y) (((x) > (y)) ? (x) : (y))
/*
- * Check the SSLVersionRange and the old style config params (nsSSL3, nsTLS1) .
- * If there are conflicts, choose the secure setting.
- */
-static void
-restrict_SSLVersionRange(void)
-{
- char mymin[VERSION_STR_LENGTH], mymax[VERSION_STR_LENGTH];
- char emin[VERSION_STR_LENGTH], emax[VERSION_STR_LENGTH];
- (void)slapi_getSSLVersion_str(slapdNSSVersions.min, mymin, sizeof(mymin));
- (void)slapi_getSSLVersion_str(slapdNSSVersions.max, mymax, sizeof(mymax));
- (void)slapi_getSSLVersion_str(enabledNSSVersions.max, emax, sizeof(emax));
- (void)slapi_getSSLVersion_str(enabledNSSVersions.min, emin, sizeof(emin));
- if (slapdNSSVersions.min > slapdNSSVersions.max) {
- slapd_SSL_warn("Invalid configured SSL range: min: %s, max: %s; "
- "Resetting the max to the supported max SSL version: %s.",
- mymin, mymax, emax);
- slapdNSSVersions.max = enabledNSSVersions.max;
- }
- if (enableSSL3) {
- if (enableTLS1) {
- if (slapdNSSVersions.min >= CURRENT_DEFAULT_SSL_VERSION) {
- slapd_SSL_warn("Configured range: min: %s, max: %s; "
- "but both nsSSL3 and nsTLS1 are on. "
- "Respect the supported range.",
- mymin, mymax);
- enableSSL3 = PR_FALSE;
- } else {
- slapd_SSL_warn("Min value is too low in range: min: %s, max: %s; "
- "We strongly recommend to set sslVersionMin higher than %s.",
- mymin, mymax, DEFVERSION);
- }
- if (slapdNSSVersions.max < CURRENT_DEFAULT_SSL_VERSION) {
- slapd_SSL_warn("Configured range: min: %s, max: %s; "
- "but both nsSSL3 and nsTLS1 are on. "
- "Resetting the max to the supported max SSL version: %s.",
- mymin, mymax, emax);
- slapdNSSVersions.max = enabledNSSVersions.max;
- }
- } else {
- /* nsTLS1 is explicitly set to off. */
- if (enabledNSSVersions.min >= CURRENT_DEFAULT_SSL_VERSION) {
- slapd_SSL_warn("Supported range: min: %s, max: %s; "
- "but nsSSL3 is on and nsTLS1 is off. "
- "Respect the supported range.",
- emin, emax);
- slapdNSSVersions.min = SSLVGreater(slapdNSSVersions.min, enabledNSSVersions.min);
- enableSSL3 = PR_FALSE;
- enableTLS1 = PR_TRUE;
- } else if (slapdNSSVersions.min >= CURRENT_DEFAULT_SSL_VERSION) {
- slapd_SSL_warn("Configured range: min: %s, max: %s; "
- "but nsSSL3 is on and nsTLS1 is off. "
- "Respect the configured range.",
- mymin, mymax);
- enableSSL3 = PR_FALSE;
- enableTLS1 = PR_TRUE;
- } else if (slapdNSSVersions.min < CURRENT_DEFAULT_SSL_VERSION) {
- slapd_SSL_warn("Min value is too low in range: min: %s, max: %s; "
- "We strongly recommend to set sslVersionMin higher than %s.",
- mymin, mymax, DEFVERSION);
- } else {
- /*
- * slapdNSSVersions.min < SSL_LIBRARY_VERSION_TLS_1_0 &&
- * slapdNSSVersions.max >= SSL_LIBRARY_VERSION_TLS_1_1
- */
- slapd_SSL_warn("Configured range: min: %s, max: %s; "
- "but nsSSL3 is on and nsTLS1 is off. "
- "Respect the configured range.",
- mymin, mymax);
- enableTLS1 = PR_TRUE;
- }
- }
- } else {
- if (enableTLS1) {
- if (enabledNSSVersions.max < CURRENT_DEFAULT_SSL_VERSION) {
- /* TLS1 is on, but TLS1 is not supported by NSS. */
- slapd_SSL_warn("Supported range: min: %s, max: %s; "
- "Setting the version range based upon the supported range.",
- emin, emax);
- slapdNSSVersions.max = enabledNSSVersions.max;
- slapdNSSVersions.min = enabledNSSVersions.min;
- enableSSL3 = PR_TRUE;
- enableTLS1 = PR_FALSE;
- } else if ((slapdNSSVersions.max < CURRENT_DEFAULT_SSL_VERSION) ||
- (slapdNSSVersions.min < CURRENT_DEFAULT_SSL_VERSION)) {
- slapdNSSVersions.max = enabledNSSVersions.max;
- slapdNSSVersions.min = SSLVGreater(CURRENT_DEFAULT_SSL_VERSION, enabledNSSVersions.min);
- slapd_SSL_warn("nsTLS1 is on, but the version range is lower than \"%s\"; "
- "Configuring the version range as default min: %s, max: %s.",
- DEFVERSION, DEFVERSION, emax);
- } else {
- /*
- * slapdNSSVersions.min >= SSL_LIBRARY_VERSION_TLS_1_0 &&
- * slapdNSSVersions.max >= SSL_LIBRARY_VERSION_TLS_1_0
- */
- ;
- }
- } else {
- slapd_SSL_info("Supported range: min: %s, max: %s; "
- "Respect the configured range.",
- emin, emax);
- /* nsTLS1 is explicitly set to off. */
- if (slapdNSSVersions.min >= CURRENT_DEFAULT_SSL_VERSION) {
- enableTLS1 = PR_TRUE;
- } else if (slapdNSSVersions.max < CURRENT_DEFAULT_SSL_VERSION) {
- enableSSL3 = PR_TRUE;
- } else {
- /*
- * slapdNSSVersions.min < SSL_LIBRARY_VERSION_TLS_1_0 &&
- * slapdNSSVersions.max >= SSL_LIBRARY_VERSION_TLS_1_0
- */
- enableSSL3 = PR_TRUE;
- enableTLS1 = PR_TRUE;
- }
- }
- }
-}
-
-/*
* slapd_nss_init() is always called from main(), even if we do not
* plan to listen on a secure port. If config_available is 0, the
* config. entries from dse.ldif are NOT available (used only when
@@ -1483,7 +1250,7 @@ slapd_ssl_init()
}
/*
- * val: sslVersionMin/Max value set in cn=encription,cn=config (INPUT)
+ * val: sslVersionMin/Max value set in cn=encryption,cn=config (INPUT)
* rval: Corresponding value to set SSLVersionRange (OUTPUT)
* ismin: True if val is sslVersionMin value
*/
@@ -1494,8 +1261,7 @@ slapd_ssl_init()
static int
set_NSS_version(char *val, PRUint16 *rval, int ismin)
{
- char *vp, *endp;
- int64_t vnum;
+ char *vp;
char emin[VERSION_STR_LENGTH], emax[VERSION_STR_LENGTH];
if (NULL == rval) {
@@ -1503,73 +1269,20 @@ set_NSS_version(char *val, PRUint16 *rval, int ismin)
}
(void)slapi_getSSLVersion_str(enabledNSSVersions.min, emin, sizeof(emin));
(void)slapi_getSSLVersion_str(enabledNSSVersions.max, emax, sizeof(emax));
- if (!strncasecmp(val, SSLSTR, SSLLEN)) { /* ssl# */
- vp = val + SSLLEN;
- vnum = strtol(vp, &endp, 10);
- if (2 == vnum) {
- if (ismin) {
- if (enabledNSSVersions.min > SSL_LIBRARY_VERSION_2) {
- slapd_SSL_warn("The value of sslVersionMin "
- "\"%s\" is lower than the supported version; "
- "the default value \"%s\" is used.",
- val, emin);
- (*rval) = enabledNSSVersions.min;
- } else {
- (*rval) = SSL_LIBRARY_VERSION_2;
- }
- } else {
- if (enabledNSSVersions.max < SSL_LIBRARY_VERSION_2) {
- /* never happens */
- slapd_SSL_warn("The value of sslVersionMax "
- "\"%s\" is higher than the supported version; "
- "the default value \"%s\" is used.",
- val, emax);
- (*rval) = enabledNSSVersions.max;
- } else {
- (*rval) = SSL_LIBRARY_VERSION_2;
- }
- }
- } else if (3 == vnum) {
- if (ismin) {
- if (enabledNSSVersions.min > SSL_LIBRARY_VERSION_3_0) {
- slapd_SSL_warn("The value of sslVersionMin "
- "\"%s\" is lower than the supported version; "
- "the default value \"%s\" is used.",
- val, emin);
- (*rval) = enabledNSSVersions.min;
- } else {
- (*rval) = SSL_LIBRARY_VERSION_3_0;
- }
- } else {
- if (enabledNSSVersions.max < SSL_LIBRARY_VERSION_3_0) {
- /* never happens */
- slapd_SSL_warn("The value of sslVersionMax "
- "\"%s\" is higher than the supported version; "
- "the default value \"%s\" is used.",
- val, emax);
- (*rval) = enabledNSSVersions.max;
- } else {
- (*rval) = SSL_LIBRARY_VERSION_3_0;
- }
- }
+
+ if (!strncasecmp(val, SSLSTR, SSLLEN)) { /* ssl# NOT SUPPORTED */
+ if (ismin) {
+ slapd_SSL_warn("SSL3 is no longer supported. Using NSS default min value: %s\n", emin);
+ (*rval) = enabledNSSVersions.min;
} else {
- if (ismin) {
- slapd_SSL_warn("The value of sslVersionMin "
- "\"%s\" is invalid; the default value \"%s\" is used.",
- val, emin);
- (*rval) = enabledNSSVersions.min;
- } else {
- slapd_SSL_warn("The value of sslVersionMax "
- "\"%s\" is invalid; the default value \"%s\" is used.",
- val, emax);
- (*rval) = enabledNSSVersions.max;
- }
+ slapd_SSL_warn("SSL3 is no longer supported. Using NSS default max value: %s\n", emax);
+ (*rval) = enabledNSSVersions.max;
}
} else if (!strncasecmp(val, TLSSTR, TLSLEN)) { /* tls# */
float tlsv;
vp = val + TLSLEN;
sscanf(vp, "%4f", &tlsv);
- if (tlsv < 1.1) { /* TLS1.0 */
+ if (tlsv < 1.1f) { /* TLS1.0 */
if (ismin) {
if (enabledNSSVersions.min > CURRENT_DEFAULT_SSL_VERSION) {
slapd_SSL_warn("The value of sslVersionMin "
@@ -1592,7 +1305,7 @@ set_NSS_version(char *val, PRUint16 *rval, int ismin)
(*rval) = CURRENT_DEFAULT_SSL_VERSION;
}
}
- } else if (tlsv < 1.2) { /* TLS1.1 */
+ } else if (tlsv < 1.2f) { /* TLS1.1 */
if (ismin) {
if (enabledNSSVersions.min > SSL_LIBRARY_VERSION_TLS_1_1) {
slapd_SSL_warn("The value of sslVersionMin "
@@ -1615,7 +1328,7 @@ set_NSS_version(char *val, PRUint16 *rval, int ismin)
(*rval) = SSL_LIBRARY_VERSION_TLS_1_1;
}
}
- } else if (tlsv < 1.3) { /* TLS1.2 */
+ } else if (tlsv < 1.3f) { /* TLS1.2 */
if (ismin) {
if (enabledNSSVersions.min > SSL_LIBRARY_VERSION_TLS_1_2) {
slapd_SSL_warn("The value of sslVersionMin "
@@ -1638,6 +1351,29 @@ set_NSS_version(char *val, PRUint16 *rval, int ismin)
(*rval) = SSL_LIBRARY_VERSION_TLS_1_2;
}
}
+ } else if (tlsv < 1.4f) { /* TLS1.3 */
+ if (ismin) {
+ if (enabledNSSVersions.min > SSL_LIBRARY_VERSION_TLS_1_3) {
+ slapd_SSL_warn("The value of sslVersionMin "
+ "\"%s\" is lower than the supported version; "
+ "the default value \"%s\" is used.",
+ val, emin);
+ (*rval) = enabledNSSVersions.min;
+ } else {
+ (*rval) = SSL_LIBRARY_VERSION_TLS_1_3;
+ }
+ } else {
+ if (enabledNSSVersions.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ /* never happens */
+ slapd_SSL_warn("The value of sslVersionMax "
+ "\"%s\" is higher than the supported version; "
+ "the default value \"%s\" is used.",
+ val, emax);
+ (*rval) = enabledNSSVersions.max;
+ } else {
+ (*rval) = SSL_LIBRARY_VERSION_TLS_1_3;
+ }
+ }
} else { /* Specified TLS is newer than supported */
if (ismin) {
slapd_SSL_warn("The value of sslVersionMin "
@@ -1683,7 +1419,9 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
CERTCertificate *cert = NULL;
SECKEYPrivateKey *key = NULL;
char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE] = {0};
- char *val = NULL;
+ const char *val = NULL;
+ char *cipher_val = NULL;
+ char *clientauth_val = NULL;
char *default_val = NULL;
int nFamilies = 0;
SECStatus sslStatus;
@@ -1722,7 +1460,7 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
slapd_SSL_error("Failed get config entry %s", configDN);
return 1;
}
- val = slapi_entry_attr_get_charptr(e, "allowWeakCipher");
+ val = slapi_fetch_attr(e, "allowWeakCipher", NULL);
if (val) {
if (!PL_strcasecmp(val, "off") || !PL_strcasecmp(val, "false") ||
!PL_strcmp(val, "0") || !PL_strcasecmp(val, "no")) {
@@ -1735,15 +1473,14 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
"Ignoring it and set it to default.", val, configDN);
}
}
- slapi_ch_free_string(&val);
/* Set SSL cipher preferences */
- if (NULL != (val = _conf_setciphers(ciphers, allowweakcipher))) {
+ if (NULL != (cipher_val = _conf_setciphers(ciphers, allowweakcipher))) {
errorCode = PR_GetError();
slapd_SSL_warn("Failed to set SSL cipher "
"preference information: %s (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
- val, errorCode, slapd_pr_strerror(errorCode));
- slapi_ch_free_string(&val);
+ cipher_val, errorCode, slapd_pr_strerror(errorCode));
+ slapi_ch_free_string(&cipher_val);
}
slapi_ch_free_string(&ciphers);
freeConfigEntry(&e);
@@ -1782,8 +1519,6 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
return -1;
}
fipsMode = PR_TRUE;
- /* FIPS does not like to use SSLv3 */
- enableSSL3 = PR_FALSE;
}
slapd_pk11_setSlotPWValues(slot, 0, 0);
@@ -1992,26 +1727,14 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
return -1;
}
- /* Explicitly disabling SSL2 - NGK */
- sslStatus = SSL_OptionSet(pr_sock, SSL_ENABLE_SSL2, enableSSL2);
- if (sslStatus != SECSuccess) {
- errorCode = PR_GetError();
- slapd_SSL_error("Failed to %s SSLv2 "
- "on the imported socket (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
- enableSSL2 ? "enable" : "disable",
- errorCode, slapd_pr_strerror(errorCode));
- return -1;
- }
-
/* Retrieve the SSL Client Authentication status from cn=config */
/* Set a default value if no value found */
getConfigEntry(configDN, &e);
- val = NULL;
if (e != NULL) {
- val = slapi_entry_attr_get_charptr(e, "nssslclientauth");
+ clientauth_val = (char *)slapi_fetch_attr(e, "nssslclientauth", NULL);
}
- if (!val) {
+ if (!clientauth_val) {
errorCode = PR_GetError();
slapd_SSL_warn("Cannot get SSL Client "
"Authentication status. No nsslclientauth in %s (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
@@ -2030,9 +1753,9 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
default_val = "allowed";
break;
}
- val = default_val;
+ clientauth_val = default_val;
}
- if (config_set_SSLclientAuth("nssslclientauth", val, errorbuf,
+ if (config_set_SSLclientAuth("nssslclientauth", clientauth_val, errorbuf,
CONFIG_APPLY) != LDAP_SUCCESS) {
errorCode = PR_GetError();
slapd_SSL_warn("Cannot set SSL Client "
@@ -2041,53 +1764,28 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
"and \"required\". (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
val, errorbuf, errorCode, slapd_pr_strerror(errorCode));
}
- if (val != default_val) {
- slapi_ch_free_string(&val);
- }
if (e != NULL) {
- val = slapi_entry_attr_get_charptr(e, "nsSSL3");
+ val = slapi_fetch_attr(e, "nsSSL3", NULL);
if (val) {
- if (!PL_strcasecmp(val, "off")) {
- enableSSL3 = PR_FALSE;
- } else if (!PL_strcasecmp(val, "on")) {
- enableSSL3 = PR_TRUE;
- } else {
- enableSSL3 = slapi_entry_attr_get_bool(e, "nsSSL3");
- }
- if (fipsMode && enableSSL3) {
- slapd_SSL_warn("FIPS mode is enabled and "
- "nsSSL3 explicitly set to on - SSLv3 is not approved "
- "for use in FIPS mode - SSLv3 will be disabled - if "
- "you want to use SSLv3, you must use modutil to "
- "disable FIPS in the internal token.");
- enableSSL3 = PR_FALSE;
+ if (!PL_strcasecmp(val, "on")) {
+ slapd_SSL_warn("NSS no longer support SSL3, the nsSSL3 setting will be ignored");
}
}
- slapi_ch_free_string(&val);
- val = slapi_entry_attr_get_charptr(e, "nsTLS1");
+ val = slapi_fetch_attr(e, "nsTLS1", NULL);
if (val) {
if (!PL_strcasecmp(val, "off")) {
- enableTLS1 = PR_FALSE;
- } else if (!PL_strcasecmp(val, "on")) {
- enableTLS1 = PR_TRUE;
- } else {
- enableTLS1 = slapi_entry_attr_get_bool(e, "nsTLS1");
+ slapd_SSL_warn("NSS only supports TLS, the nsTLS1 setting of \"off\" will be ignored");
}
- } else if (enabledNSSVersions.max >= CURRENT_DEFAULT_SSL_VERSION) {
- enableTLS1 = PR_TRUE; /* If available, enable TLS1 */
}
- slapi_ch_free_string(&val);
- val = slapi_entry_attr_get_charptr(e, "sslVersionMin");
+ val = slapi_fetch_attr(e, "sslVersionMin", NULL);
if (val) {
- (void)set_NSS_version(val, &NSSVersionMin, 1);
+ (void)set_NSS_version((char *)val, &NSSVersionMin, 1);
}
- slapi_ch_free_string(&val);
- val = slapi_entry_attr_get_charptr(e, "sslVersionMax");
+ val = slapi_fetch_attr(e, "sslVersionMax", NULL);
if (val) {
- (void)set_NSS_version(val, &NSSVersionMax, 0);
+ (void)set_NSS_version((char *)val, &NSSVersionMax, 0);
}
- slapi_ch_free_string(&val);
if (NSSVersionMin > NSSVersionMax) {
(void)slapi_getSSLVersion_str(NSSVersionMin, mymin, sizeof(mymin));
(void)slapi_getSSLVersion_str(NSSVersionMax, mymax, sizeof(mymax));
@@ -2103,7 +1801,6 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
/* Handle the SSL version range */
slapdNSSVersions.min = NSSVersionMin;
slapdNSSVersions.max = NSSVersionMax;
- restrict_SSLVersionRange();
(void)slapi_getSSLVersion_str(slapdNSSVersions.min, mymin, sizeof(mymin));
(void)slapi_getSSLVersion_str(slapdNSSVersions.max, mymax, sizeof(mymax));
slapi_log_err(SLAPI_LOG_INFO, "Security Initialization",
@@ -2122,7 +1819,7 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
*/
sslStatus = SSL_VersionRangeGet(pr_sock, &slapdNSSVersions);
if (sslStatus == SECSuccess) {
- if (slapdNSSVersions.max > LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 && slapd_pk11_isFIPS()) {
+ if (slapdNSSVersions.max > LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 && fipsMode) {
/*
* FIPS & NSS currently only support a max version of TLS1.2
* (although NSS advertises 1.3 as a max range in FIPS mode),
@@ -2155,7 +1852,7 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
val = NULL;
if (e != NULL) {
- val = slapi_entry_attr_get_charptr(e, "nsTLSAllowClientRenegotiation");
+ val = slapi_fetch_attr(e, "nsTLSAllowClientRenegotiation", NULL);
}
if (val) {
/* We default to allowing reneg. If the option is "no",
@@ -2170,7 +1867,6 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS)
renegotiation = SSL_RENEGOTIATE_REQUIRES_XTN;
}
}
- slapi_ch_free_string(&val);
sslStatus = SSL_OptionSet(pr_sock, SSL_ENABLE_RENEGOTIATION, (PRBool)renegotiation);
if (sslStatus != SECSuccess) {
diff --git a/src/lib389/lib389/instance/remove.py b/src/lib389/lib389/instance/remove.py
index 378cd64..e85e866 100644
--- a/src/lib389/lib389/instance/remove.py
+++ b/src/lib389/lib389/instance/remove.py
@@ -30,7 +30,7 @@ def remove_ds_instance(dirsrv, force=False):
:param dirsrv: A directory server instance
:type dirsrv: DirSrv
- :param force: A psycological aid, for people who think force means do something, harder. Does
+ :param force: A psychological aid, for people who think force means do something, harder. Does
literally nothing in this program because state machines are a thing.
:type force: bool
"""
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] branch 389-ds-base-1.4.0 updated: Issue 50325 - Add Security tab to UI
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.4.0
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.4.0 by this push:
new 60ce15b Issue 50325 - Add Security tab to UI
60ce15b is described below
commit 60ce15b8a39099e038b4284a844906bc330ce624
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Wed Jul 3 16:36:53 2019 -0400
Issue 50325 - Add Security tab to UI
Description: This updates the CLI and UI to handle a majority of
the security configuration. It also adds support
for PF dual list selection even though I ended up
not using it.
Relates: https://pagure.io/389-ds-base/issue/50325
Reviewed by: spichugi, and mhonek (Thanks!!)
Fixed Simon's issues
Fix issue with listing certs with spaces in the name
Fix npm vulnerabilities
Fix selinux port labeling, and add 'saving' spinners
Use a regex for parsing certutil output
---
src/cockpit/389-console/.babelrc | 5 +-
src/cockpit/389-console/package-lock.json | 117 +++
src/cockpit/389-console/package.json | 2 +
src/cockpit/389-console/src/css/ds.css | 186 ++++-
src/cockpit/389-console/src/ds.js | 6 +-
src/cockpit/389-console/src/index.es6 | 7 +
src/cockpit/389-console/src/index.html | 19 +-
.../src/lib/database/databaseTables.jsx | 7 +-
.../src/lib/security/certificateManagement.jsx | 617 +++++++++++++++
.../389-console/src/lib/security/ciphers.jsx | 274 +++++++
.../src/lib/security/securityModals.jsx | 689 +++++++++++++++++
.../src/lib/security/securityTables.jsx | 454 +++++++++++
src/cockpit/389-console/src/lib/tools.jsx | 18 +-
src/cockpit/389-console/src/security.html | 502 ------------
src/cockpit/389-console/src/security.js | 137 ----
src/cockpit/389-console/src/security.jsx | 853 +++++++++++++++++++++
src/cockpit/389-console/webpack.config.js | 15 +-
src/lib389/cli/dsconf | 3 -
src/lib389/lib389/__init__.py | 27 +-
src/lib389/lib389/cli_conf/security.py | 298 ++++++-
src/lib389/lib389/config.py | 28 +-
src/lib389/lib389/nss_ssl.py | 156 +++-
src/lib389/lib389/utils.py | 8 +-
23 files changed, 3674 insertions(+), 754 deletions(-)
diff --git a/src/cockpit/389-console/.babelrc b/src/cockpit/389-console/.babelrc
index d0ef093..23c75c5 100644
--- a/src/cockpit/389-console/.babelrc
+++ b/src/cockpit/389-console/.babelrc
@@ -1,4 +1,7 @@
{
"presets": ["@babel/env",
- "@babel/preset-react"]
+ "@babel/preset-react"],
+ "plugins": [
+ "@babel/plugin-proposal-class-properties"
+ ]
}
diff --git a/src/cockpit/389-console/package-lock.json b/src/cockpit/389-console/package-lock.json
index a35d857..16a54ab 100644
--- a/src/cockpit/389-console/package-lock.json
+++ b/src/cockpit/389-console/package-lock.json
@@ -104,6 +104,105 @@
"@babel/types": "^7.0.0"
}
},
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/h...",
+ "integrity": "sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.4.4",
+ "@babel/helper-split-export-declaration": "^7.4.4"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz",
+ "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.4.4",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.11",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-...",
+ "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/traverse": "^7.4.4",
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helpe...",
+ "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz",
+ "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==",
+ "dev": true
+ },
+ "@babel/traverse": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz",
+ "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.4.4",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.4.4",
+ "@babel/parser": "^7.4.5",
+ "@babel/types": "^7.4.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.11"
+ }
+ },
+ "@babel/types": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
+ "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
"@babel/helper-define-map": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7...",
@@ -336,6 +435,16 @@
"@babel/plugin-syntax-async-generators": "^7.0.0"
}
},
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plug...",
+ "integrity": "sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.4.4",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
"@babel/plugin-proposal-json-strings": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-p...",
@@ -7180,6 +7289,14 @@
"warning": "^3.0.0"
}
},
+ "react-switch": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-5.0.0.tgz",
+ "integrity": "sha512-+zxY9xj9dMc8Y4gv/kkqQrirfEiIQ+SlQfJDW1Wi81L3xoh1fcbBYyJyh0TnhM/U/b6HxuBmkmU4Ooxgtuoavw==",
+ "requires": {
+ "prop-types": "^15.6.2"
+ }
+ },
"react-transition-group": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-grou...",
diff --git a/src/cockpit/389-console/package.json b/src/cockpit/389-console/package.json
index 5ce97c8..eb269d9 100644
--- a/src/cockpit/389-console/package.json
+++ b/src/cockpit/389-console/package.json
@@ -19,6 +19,7 @@
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
"ajv": "^6.0.0",
"audit-ci": "^1.7.0",
"babel-eslint": "^9.0.0",
@@ -57,6 +58,7 @@
"react-bootstrap": "0.32.4",
"react-bootstrap-typeahead": "3.2.4",
"react-dom": "16.6.1",
+ "react-switch": "^5.0.0",
"recompose": "0.30.0",
"table-resolver": "4.1.1"
}
diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css
index 1ad8d5c..f8945bf 100644
--- a/src/cockpit/389-console/src/css/ds.css
+++ b/src/cockpit/389-console/src/css/ds.css
@@ -44,7 +44,7 @@
/* Main nav page index.html */
.ds-content {
padding: 0;
- padding-top: 115px;
+ padding-top: 115px; /* this pushes the content below fixed nav bar */
padding-bottom: 50px;
margin-top: 0;
margin-left: 25px;
@@ -672,6 +672,11 @@ td {
padding-bottom: 10px;
}
+.ds-cipher-width {
+ max-width: 350px !important;
+ min-width: 350px !important;
+}
+
/*
* Popup modal stuff
*/
@@ -1637,8 +1642,8 @@ option {
font-size: 16px;
}
-.ds-no-padding () {
- padding: 0 !imporant;
+.ds-no-padding {
+ padding-right: 0 !important;
}
.alert {
@@ -1650,6 +1655,10 @@ option {
margin-top: 5%;
}
+.ds-select {
+ width: 120px;
+}
+
.treeview .list-group-item {
/* remove focus border */
outline: none;
@@ -1680,3 +1689,174 @@ input {
.ds-width-auto {
width: 100%;
}
+
+/* Dual List CSS */
+.dual-list-pf-arrows {
+ display: inline-block;
+ margin: auto;
+ position: relative;
+ bottom: 170px;
+ font-size: 23px;
+ color: #bbb;
+}
+@media only screen and (max-width: 600px) {
+ .dual-list-pf-arrows {
+ display: block;
+ position: inherit;
+ margin: 5px 0;
+ padding-left: 79px;
+ }
+}
+
+.dual-list-pf-arrows span {
+ display: block;
+ margin: 25px;
+ cursor: pointer;
+ transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+ transform: rotate(-90deg);
+}
+@media only screen and (max-width: 600px) {
+ .dual-list-pf-arrows span {
+ display: inline;
+ margin: 0 20px 0 0;
+ }
+}
+
+.dual-list-pf-arrows span:hover {
+ color: #8b8d8f;
+}
+
+.dual-list-pf-body {
+ height: 375px;
+ width: 320px;
+ overflow-y: scroll;
+ overflow-x: auto;
+ display: inline-grid;
+ align-content: flex-start;
+}
+
+.dual-list-pf-body::-webkit-scrollbar {
+ width: 12px;
+ height: 12px;
+ background: #fafafa;
+}
+
+.dual-list-pf-body::-webkit-scrollbar-thumb {
+ background: #d1d1d1;
+ border-radius: 6px;
+ border: 3px solid transparent;
+ background-clip: content-box;
+}
+
+.dual-list-pf-body::-webkit-scrollbar-thumb:hover {
+ background: #bbb;
+ border-radius: 6px;
+ border: 3px solid transparent;
+ background-clip: content-box;
+}
+
+.dual-list-pf-filter {
+ margin-left: 20px;
+}
+
+.dual-list-pf-filter input {
+ background-color: #f5f5f5;
+ border: 1px solid #ededed;
+ width: 145px;
+ padding: 0 22px 0 5px;
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+
+.dual-list-pf-filter .search-icon {
+ position: relative;
+ right: 20px;
+ bottom: 1px;
+ color: #bbb;
+}
+
+.dual-list-pf-filter ::-webkit-input-placeholder {
+ font-style: italic;
+}
+
+.dual-list-pf-footer {
+ padding: 10px;
+ border-top: 1px solid #d1d1d1;
+}
+
+.dual-list-pf-heading {
+ border-bottom: 1px solid #d1d1d1;
+}
+
+.dual-list-pf-item {
+ padding: 5px 0;
+ margin-bottom: 0;
+ font-weight: 400;
+ transition: background 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), color 0.3s ease-out;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.dual-list-pf-item input[type='checkbox'] {
+ position: relative;
+ left: 10px;
+ vertical-align: top;
+ cursor: pointer;
+}
+
+.dual-list-pf-item.selected {
+ background-color: #0088ce;
+ color: white;
+}
+
+.dual-list-pf-item.disabled {
+ cursor: not-allowed;
+ background: #f5f5f5;
+ color: #8b8d8f;
+}
+
+.dual-list-pf-item.disabled input[type='checkbox'] {
+ cursor: not-allowed;
+}
+
+.dual-list-pf-item.child {
+ padding-left: 22px;
+}
+
+.dual-list-pf-item:hover:not(.selected):not(.disabled) {
+ background-color: #bee1f4;
+ color: inherit;
+}
+
+.dual-list-pf-item-label {
+ margin-left: 20px;
+}
+
+.dual-list-pf-main-checkbox {
+ position: relative;
+ left: 10px;
+ vertical-align: text-top;
+ cursor: pointer;
+}
+
+.dual-list-pf-no-items {
+ margin-top: 30px;
+ text-align: center;
+}
+
+.dual-list-pf-selector {
+ display: inline-block;
+ border: 1px solid #d1d1d1;
+ user-select: none;
+}
+
+.dual-list-pf-sort-icon {
+ cursor: pointer;
+}
+
+.dropdown-kebab-pf.btn-group {
+ margin-left: 10px;
+ float: right;
+ margin-right: 10px;
+}
+/* End of dual list */
diff --git a/src/cockpit/389-console/src/ds.js b/src/cockpit/389-console/src/ds.js
index ec57488..e023fdc 100644
--- a/src/cockpit/389-console/src/ds.js
+++ b/src/cockpit/389-console/src/ds.js
@@ -8,7 +8,7 @@ var dn_regex = new RegExp( "^([A-Za-z]+=.*)" );
* to track the loading, and once all the pages are loaded, then we can load the config
*/
var server_page_loaded = 0;
-var security_page_loaded = 0;
+var security_page_loaded = 1;
var db_page_loaded = 1;
var repl_page_loaded = 0;
var plugin_page_loaded = 1;
@@ -482,4 +482,8 @@ $(window.document).ready(function() {
$(".all-pages").hide();
$("#monitor-content").show();
});
+ $("#security-tab").on("click", function() {
+ $(".all-pages").hide();
+ $("#security-content").show();
+ });
});
diff --git a/src/cockpit/389-console/src/index.es6 b/src/cockpit/389-console/src/index.es6
index 0483752..71e9c5e 100644
--- a/src/cockpit/389-console/src/index.es6
+++ b/src/cockpit/389-console/src/index.es6
@@ -3,6 +3,7 @@ import ReactDOM from "react-dom";
import { Plugins } from "./plugins.jsx";
import { Database } from "./database.jsx";
import { Monitor } from "./monitor.jsx";
+import { Security } from "./security.jsx";
var serverIdElem;
@@ -35,6 +36,12 @@ function renderReactDOM(clear) {
<Monitor serverId={serverIdElem} key={tabKey} />,
document.getElementById("monitor")
);
+
+ // Security tab
+ ReactDOM.render(
+ <Security serverId={serverIdElem} key={tabKey} />,
+ document.getElementById("security")
+ );
}
// We have to create the wrappers because this is no simple way
diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html
index 7c5dbf8..c93f754 100644
--- a/src/cockpit/389-console/src/index.html
+++ b/src/cockpit/389-console/src/index.html
@@ -21,7 +21,6 @@
<script src="ds.js"></script>
<script src="schema.js"></script>
<script src="servers.js"></script>
- <script src="security.js"></script>
<script src="replication.js"></script>
<link href="static/bootstrap.min.css" rel="stylesheet">
<link href="static/jquery.dataTables.min.css" type="text/css" rel="stylesheet">
@@ -80,23 +79,10 @@
</li>
<!-- Security navtab -->
- <li class="dropdown ds-nav-tab">
- <a href="#0" class="dropdown-toggle ds-tab-list" data-toggle="dropdown" id="security-tab">
+ <li class="ds-nav-tab">
+ <a href="#0" class="ds-tab-list ds-tab-standalone" id="security-tab">
Security
- <b class="caret"></b>
</a>
- <ul class="dropdown-menu ds-nav-item">
- <li><a href="#0" class="ds-nav-choice" id="sec-config-btn" parent-id="security-tab">Security Settings</a></li>
- <li class="dropdown-submenu">
- <a tabindex="-1" href="#0">Certificate Management</a>
- <ul class="dropdown-menu">
- <li><a href="#0" class="ds-nav-choice" id="sec-cacert-btn" parent-id="security-tab">CA Certificates</a></li>
- <li><a href="#0" class="ds-nav-choice" id="sec-srvcert-btn" parent-id="security-tab">Server Certificates</a></li>
- <li><a href="#0" class="ds-nav-choice" id="sec-revoked-btn" parent-id="security-tab">Revoked Certificates</a></li>
- </ul>
- </li>
- <li><a href="#0" class="ds-nav-choice" id="sec-ciphers-btn" parent-id="security-tab">Supported Ciphers</a></li>
- </ul>
</li>
<!-- Database navtab -->
@@ -501,6 +487,7 @@
</div>
<div id="security-content" class="all-pages" hidden>
+ <div id="security"></div>
</div>
<div id="database-content" class="all-pages" hidden>
diff --git a/src/cockpit/389-console/src/lib/database/databaseTables.jsx b/src/cockpit/389-console/src/lib/database/databaseTables.jsx
index adf535e..5d90f02 100644
--- a/src/cockpit/389-console/src/lib/database/databaseTables.jsx
+++ b/src/cockpit/389-console/src/lib/database/databaseTables.jsx
@@ -53,7 +53,7 @@ class ReferralTable extends React.Component {
},
cell: {
props: {
- index: 2
+ index: 1
},
formatters: [
(value, { rowData }) => {
@@ -75,6 +75,7 @@ class ReferralTable extends React.Component {
]
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -345,6 +346,7 @@ class EncryptedAttrTable extends React.Component {
]
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -508,6 +510,7 @@ class LDIFTable extends React.Component {
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -703,6 +706,7 @@ class LDIFManageTable extends React.Component {
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
} // Constructor
getColumns() {
@@ -877,6 +881,7 @@ class BackupTable extends React.Component {
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
} // Constructor
getColumns() {
diff --git a/src/cockpit/389-console/src/lib/security/certificateManagement.jsx b/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
new file mode 100644
index 0000000..d5cd927
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
@@ -0,0 +1,617 @@
+import cockpit from "cockpit";
+import React from "react";
+import {
+ Nav,
+ NavItem,
+ TabContainer,
+ TabContent,
+ TabPane,
+ Button,
+ Spinner,
+ noop
+} from "patternfly-react";
+import { ConfirmPopup } from "../../lib/notifications.jsx";
+import {
+ CertTable
+} from "./securityTables.jsx";
+import {
+ EditCertModal,
+ SecurityAddCertModal,
+ SecurityAddCACertModal,
+} from "./securityModals.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { log_cmd } from "../../lib/tools.jsx";
+
+export class CertificateManagement extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeKey: 1,
+ ServerCerts: this.props.ServerCerts,
+ CACerts: this.props.CACerts,
+ showEditModal: false,
+ showAddModal: false,
+ modalSpinner: false,
+ showConfirmDelete: false,
+ certName: "",
+ certFile: "",
+ flags: "",
+ errObj: {},
+ isCACert: false,
+ showConfirmCAChange: false,
+ loading: false,
+ };
+
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ this.addCACert = this.addCACert.bind(this);
+ this.handleAddChange = this.handleAddChange.bind(this);
+ this.addCert = this.addCert.bind(this);
+ this.showAddModal = this.showAddModal.bind(this);
+ this.closeAddModal = this.closeAddModal.bind(this);
+ this.showAddCAModal = this.showAddCAModal.bind(this);
+ this.closeAddCAModal = this.closeAddCAModal.bind(this);
+ this.showEditModal = this.showEditModal.bind(this);
+ this.closeEditModal = this.closeEditModal.bind(this);
+ this.showEditCAModal = this.showEditCAModal.bind(this);
+ this.handleFlagChange = this.handleFlagChange.bind(this);
+ this.editCert = this.editCert.bind(this);
+ this.doEditCert = this.doEditCert.bind(this);
+ this.closeConfirmCAChange = this.closeConfirmCAChange.bind(this);
+ this.showDeleteConfirm = this.showDeleteConfirm.bind(this);
+ this.delCert = this.delCert.bind(this);
+ this.closeConfirmDelete = this.closeConfirmDelete.bind(this);
+ this.reloadCerts = this.reloadCerts.bind(this);
+ this.reloadCACerts = this.reloadCACerts.bind(this);
+ }
+
+ handleNavSelect(key) {
+ this.setState({
+ activeKey: key
+ });
+ }
+
+ showAddModal () {
+ this.setState({
+ showAddModal: true,
+ errObj: {certName: true, certFile: true}
+ });
+ }
+
+ closeAddModal () {
+ this.setState({
+ showAddModal: false,
+ certName: "",
+ certFile: "",
+ });
+ }
+
+ showAddCAModal () {
+ this.setState({
+ showAddCAModal: true,
+ errObj: {certName: true, certFile: true}
+ });
+ }
+
+ closeAddCAModal () {
+ this.setState({
+ showAddCAModal: false,
+ certName: "",
+ certFile: "",
+ });
+ }
+
+ addCert () {
+ if (this.state.certName == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate nickname`
+ );
+ return;
+ } else if (this.state.certFile == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate file name`
+ );
+ return;
+ }
+
+ this.setState({
+ modalSpinner: true,
+ loading: true,
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "add", "--name=" + this.state.certName, "--file=" + this.state.certFile
+ ];
+ log_cmd("addCert", "Adding server cert", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ showAddModal: false,
+ certFile: '',
+ certName: '',
+ modalSpinner: false
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully added certificate`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error adding certificate - ${msg}`
+ );
+ });
+ }
+
+ addCACert () {
+ if (this.state.certName == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate nickname`
+ );
+ return;
+ } else if (this.state.certFile == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate file name`
+ );
+ return;
+ }
+
+ this.setState({
+ modalSpinner: true,
+ loading: true,
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ca-certificate", "add", "--name=" + this.state.certName, "--file=" + this.state.certFile
+ ];
+ log_cmd("addCACert", "Adding CA certificate", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ showAddCAModal: false,
+ certFile: '',
+ certName: '',
+ modalSpinner: false,
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully added certificate`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error adding certificate - ${msg}`
+ );
+ });
+ }
+
+ showDeleteConfirm(dataRow) {
+ this.setState({
+ showConfirmDelete: true,
+ certName: dataRow.nickname[0],
+ });
+ }
+
+ delCert () {
+ this.setState({
+ modalSpinner: true,
+ loading: true
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "del", this.state.certName
+ ];
+ log_cmd("delCert", "Deleting certificate", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ certName: '',
+ modalSpinner: false,
+ showConfirmDelete: false,
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully deleted certificate`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ certName: '',
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error deleting certificate - ${msg}`
+ );
+ });
+ }
+
+ showEditModal (rowData) {
+ this.setState({
+ showEditModal: true,
+ certName: rowData.nickname[0],
+ flags: rowData.flags[0],
+ isCACert: false,
+ });
+ }
+
+ closeEditModal () {
+ this.setState({
+ showEditModal: false,
+ flags: ''
+ });
+ }
+
+ showEditCAModal (rowData) {
+ this.setState({
+ showEditModal: true,
+ certName: rowData.nickname[0],
+ flags: rowData.flags[0],
+ isCACert: true,
+ });
+ }
+
+ editCert () {
+ // Check if CA cert flags were removed
+ if (this.state.isCACert) {
+ let SSLFlags = '';
+ SSLFlags = this.state.flags.split(',', 1);
+ if (!SSLFlags[0].includes('C') || !SSLFlags[0].includes('T')) {
+ // This could remove the CA cert properties, better warn user
+ this.setState({
+ showConfirmCAChange: true
+ });
+ return;
+ }
+ }
+ this.doEditCert();
+ }
+
+ closeConfirmCAChange () {
+ this.setState({
+ showConfirmCAChange: false
+ });
+ }
+
+ doEditCert () {
+ this.setState({
+ modalSpinner: true,
+ loading: true,
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "set-trust-flags", this.state.certName, "--flags=" + this.state.flags
+ ];
+ log_cmd("doEditCert", "Editing trust flags", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ showEditModal: false,
+ flags: '',
+ certName: '',
+ modalSpinner: false,
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully changed certificate's trust flags`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ showEditModal: false,
+ flags: '',
+ certName: '',
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error setting trust flags - ${msg}`
+ );
+ });
+ }
+
+ handleAddChange (e) {
+ const value = e.target.value;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ handleFlagChange (e) {
+ const checked = e.target.checked;
+ const id = e.target.id;
+ let flags = this.state.flags;
+ let SSLFlags = '';
+ let EmailFlags = '';
+ let OSFlags = '';
+ [SSLFlags, EmailFlags, OSFlags] = flags.split(',');
+
+ if (id.endsWith('SSL')) {
+ for (let trustFlag of ['C', 'T', 'c', 'P', 'p']) {
+ if (id.startsWith(trustFlag)) {
+ if (checked) {
+ SSLFlags += trustFlag;
+ } else {
+ SSLFlags = SSLFlags.replace(trustFlag, '');
+ }
+ }
+ }
+ } else if (id.endsWith('Email')) {
+ for (let trustFlag of ['C', 'T', 'c', 'P', 'p']) {
+ if (id.startsWith(trustFlag)) {
+ if (checked) {
+ EmailFlags += trustFlag;
+ } else {
+ EmailFlags = EmailFlags.replace(trustFlag, '');
+ }
+ }
+ }
+ } else {
+ // Object Signing (OS)
+ for (let trustFlag of ['C', 'T', 'c', 'P', 'p']) {
+ if (id.startsWith(trustFlag)) {
+ if (checked) {
+ OSFlags += trustFlag;
+ } else {
+ OSFlags = OSFlags.replace(trustFlag, '');
+ }
+ }
+ }
+ }
+ this.setState({
+ flags: SSLFlags + "," + EmailFlags + "," + OSFlags
+ });
+ }
+
+ closeConfirmDelete () {
+ this.setState({
+ showConfirmDelete: false,
+ });
+ }
+
+ reloadCerts () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "list",
+ ];
+ log_cmd("reloadCerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const certs = JSON.parse(content);
+ let certNames = [];
+ for (let cert of certs) {
+ certNames.push(cert.attrs['nickname']);
+ }
+ this.setState({
+ ServerCerts: certs,
+ loading: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error loading server certificates - ${msg}`
+ );
+ });
+ }
+
+ reloadCACerts () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ca-certificate", "list",
+ ];
+ log_cmd("reloadCACerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let certs = JSON.parse(content);
+ this.setState({
+ CACerts: certs,
+ loading: false
+ }, this.reloadCerts);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error loading CA certificates - ${msg}`
+ );
+ });
+ }
+
+ render () {
+ let CATitle = 'Trusted Certificate Authorites <font size="1">(' + this.state.CACerts.length + ')</font>';
+ let ServerTitle = 'TLS Certificates <font size="1">(' + this.state.ServerCerts.length + ')</font>';
+
+ let certificatePage = '';
+
+ if (this.state.loading) {
+ certificatePage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading certificates ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ certificatePage =
+ <div className="container-fluid">
+ <div className="ds-tab-table">
+ <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: CATitle}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: ServerTitle}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-lg">
+ <CertTable
+ certs={this.state.CACerts}
+ key={this.state.CACerts}
+ editCert={this.showEditCAModal}
+ delCert={this.showDeleteConfirm}
+ />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top-med"
+ onClick={() => {
+ this.showAddCAModal();
+ }}
+ >
+ Add CA Certificate
+ </Button>
+ </div>
+ </TabPane>
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <CertTable
+ certs={this.state.ServerCerts}
+ key={this.state.ServerCerts}
+ editCert={this.showEditModal}
+ delCert={this.showDeleteConfirm}
+ />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top-med"
+ onClick={() => {
+ this.showAddModal();
+ }}
+ >
+ Add Server Certificate
+ </Button>
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ </div>;
+ }
+ return (
+ <div>
+ {certificatePage}
+ <EditCertModal
+ showModal={this.state.showEditModal}
+ closeHandler={this.closeEditModal}
+ handleChange={this.handleFlagChange}
+ saveHandler={this.editCert}
+ flags={this.state.flags}
+ spinning={this.state.modalSpinner}
+ />
+ <SecurityAddCertModal
+ showModal={this.state.showAddModal}
+ closeHandler={this.closeAddModal}
+ handleChange={this.handleAddChange}
+ saveHandler={this.addCert}
+ spinning={this.state.modalSpinner}
+ error={this.state.errObj}
+ />
+ <SecurityAddCACertModal
+ showModal={this.state.showAddCAModal}
+ closeHandler={this.closeAddCAModal}
+ handleChange={this.handleAddChange}
+ saveHandler={this.addCACert}
+ spinning={this.state.modalSpinner}
+ error={this.state.errObj}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmDelete}
+ closeHandler={this.closeConfirmDelete}
+ actionFunc={this.delCert}
+ msg="Are you sure you want to delete this certificate?"
+ msgContent={this.state.certName}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmCAChange}
+ closeHandler={this.closeConfirmCAChange}
+ actionFunc={this.doEditCert}
+ msg="Removing the 'C' or 'T' flags from the SSL trust catagory could break all TLS connectivity to and from the server, are you sure you want to proceed?"
+ />
+ </div>
+ );
+ }
+}
+
+// Props and defaults
+
+CertificateManagement.propTypes = {
+ serverId: PropTypes.string,
+ CACerts: PropTypes.array,
+ ServerCerts: PropTypes.array,
+ addNotification: PropTypes.func,
+};
+
+CertificateManagement.defaultProps = {
+ serverId: "",
+ CACerts: [],
+ ServerCerts: [],
+ addNotification: noop,
+};
+
+export default CertificateManagement;
diff --git a/src/cockpit/389-console/src/lib/security/ciphers.jsx b/src/cockpit/389-console/src/lib/security/ciphers.jsx
new file mode 100644
index 0000000..4714fcb
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/ciphers.jsx
@@ -0,0 +1,274 @@
+import React from "react";
+import cockpit from "cockpit";
+import {
+ Button,
+ Row,
+ Col,
+ ControlLabel,
+ Spinner,
+ noop,
+} from "patternfly-react";
+import { log_cmd } from "../../lib/tools.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { Typeahead } from "react-bootstrap-typeahead";
+
+export class Ciphers extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ allowCiphers: [],
+ denyCiphers: [],
+ cipherPref: "default",
+ prefs: this.props.cipherPref,
+ saving: false,
+ };
+
+ this.handlePrefChange = this.handlePrefChange.bind(this);
+ this.saveCipherPref = this.saveCipherPref.bind(this);
+ }
+
+ componentWillMount () {
+ let cipherPref = "default";
+ let allowedCiphers = [];
+ let deniedCiphers = [];
+
+ // Parse SSL cipher attributes (nsSSL3Ciphers)
+ if (this.props.cipherPref != "") {
+ let rawCiphers = this.props.cipherPref.split(",");
+
+ // First check the first element as it has special meaning
+ if (rawCiphers[0].toLowerCase() == "default") {
+ rawCiphers.shift();
+ } else if (rawCiphers[0].toLowerCase() == "+all") {
+ cipherPref = "+all";
+ rawCiphers.shift();
+ } else if (rawCiphers[0].toLowerCase() == "-all") {
+ cipherPref = "-all";
+ rawCiphers.shift();
+ }
+
+ // Process the remaining ciphers
+ rawCiphers = rawCiphers.map(function(x) { return x.toUpperCase() });
+ for (let cipher of rawCiphers) {
+ if (cipher.startsWith("+")) {
+ allowedCiphers.push(cipher.substring(1));
+ } else if (cipher.startsWith("-")) {
+ deniedCiphers.push(cipher.substring(1));
+ }
+ }
+ }
+
+ this.setState({
+ cipherPref: cipherPref,
+ allowCiphers: allowedCiphers,
+ denyCiphers: deniedCiphers,
+ });
+ }
+
+ saveCipherPref () {
+ /* start the spinner */
+ this.setState({
+ saving: true
+ });
+ let prefs = this.state.cipherPref;
+ for (let cipher of this.state.allowCiphers) {
+ prefs += ",+" + cipher;
+ }
+ for (let cipher of this.state.denyCiphers) {
+ prefs += ",-" + cipher;
+ }
+
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ciphers", "set", "--", prefs
+ ];
+ log_cmd("saveCipherPref", "Saving cipher preferences", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.props.addNotification(
+ "success",
+ `Successfully set cipher preferences. You must restart the server for these changes to take effect.`
+ );
+ this.setState({
+ saving: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error setting cipher preferences - ${msg}`
+ );
+ this.setState({
+ saving: false,
+ });
+ });
+ }
+
+ handlePrefChange (e) {
+ this.setState({
+ cipherPref: e.target.value,
+ });
+ }
+
+ render () {
+ let supportedCiphers = [];
+ let enabledCiphers = [];
+ let cipherPage;
+
+ for (let cipher of this.props.supportedCiphers) {
+ if (!this.props.enabledCiphers.includes(cipher)) {
+ // This cipher is not currently enabled, so list it as available
+ supportedCiphers.push(cipher);
+ }
+ }
+ for (let cipher of this.props.enabledCiphers) {
+ enabledCiphers.push(cipher);
+ }
+ let supportedList = supportedCiphers.map((name) =>
+ <option key={name}>{name}</option>
+ );
+ let enabledList = enabledCiphers.map((name) =>
+ <option key={name}>{name}</option>
+ );
+
+ if (this.state.saving) {
+ cipherPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Saving cipher preferences ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ cipherPage =
+ <div className="container-fluid">
+ <div className="ds-container">
+ <div className='ds-inline'>
+ <div>
+ <h4>Enabled Ciphers</h4>
+ </div>
+ <div>
+ <select
+ className="ds-cipher-width"
+ size="16"
+ title="The current ciphers the server is accepting. This is only updated after a server restart"
+ >
+ {enabledList}
+ </select>
+ </div>
+ </div>
+ <div className="ds-divider-lrg" />
+ <div className='ds-inline'>
+ <div>
+ <h4>Other Available Ciphers</h4>
+ </div>
+ <div>
+ <select className="ds-cipher-width" size="16">
+ {supportedList}
+ </select>
+ </div>
+ </div>
+ </div>
+ <hr />
+ <Row>
+ <Col componentClass={ControlLabel} sm={2}>
+ Cipher Suite
+ </Col>
+ <Col sm={9}>
+ <select
+ id="cipherPref"
+ onChange={this.handlePrefChange}
+ defaultValue={this.state.cipherPref}
+ >
+ <option title="default" value="default" key="default">Default Ciphers</option>
+ <option title="+all" value="+all" key="all">All Ciphers</option>
+ <option title="-all" value="-all" key="none">No Ciphers</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={2}>
+ Allow Specific Ciphers
+ </Col>
+ <Col sm={9}>
+ <Typeahead
+ multiple
+ onChange={value => {
+ this.setState({
+ allowCiphers: value
+ });
+ }}
+ selected={this.state.allowCiphers}
+ options={this.props.supportedCiphers}
+ newSelectionPrefix="Add a cipher: "
+ placeholder="Type a cipher..."
+ id="allowCipher"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={2}>
+ Deny Specific Ciphers
+ </Col>
+ <Col sm={9}>
+ <Typeahead
+ multiple
+ onChange={value => {
+ this.setState({
+ denyCiphers: value
+ });
+ }}
+ selected={this.state.denyCiphers}
+ options={this.props.supportedCiphers}
+ newSelectionPrefix="Add a cipher: "
+ placeholder="Type a cipher..."
+ id="denyCipher"
+ />
+ </Col>
+ </Row>
+ <p />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top"
+ onClick={() => {
+ this.saveCipherPref();
+ }}
+ >
+ Save Cipher Preferences
+ </Button>
+ </div>;
+ }
+
+ return (
+ <div>
+ {cipherPage}
+ </div>
+ );
+ }
+}
+
+// Props and defaults
+
+Ciphers.propTypes = {
+ serverId: PropTypes.string,
+ supportedCiphers: PropTypes.array,
+ enabledCiphers: PropTypes.array,
+ cipherPref: PropTypes.string,
+ addNotification: PropTypes.func,
+};
+
+Ciphers.defaultProps = {
+ serverId: "",
+ supportedCiphers: [],
+ enabledCiphers: [],
+ cipherPref: "",
+ addNotification: noop,
+};
+
+export default Ciphers;
diff --git a/src/cockpit/389-console/src/lib/security/securityModals.jsx b/src/cockpit/389-console/src/lib/security/securityModals.jsx
new file mode 100644
index 0000000..f8ad49c
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/securityModals.jsx
@@ -0,0 +1,689 @@
+import React from "react";
+import {
+ Modal,
+ Row,
+ Col,
+ ControlLabel,
+ Checkbox,
+ FormControl,
+ Icon,
+ Button,
+ Form,
+ Spinner,
+ noop
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+
+export class SecurityAddCACertModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ error
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Adding CA certificate...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Add Certificate Authority
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>
+ Add CA certificate to the security database.
+ </h4>
+ <hr />
+ <Row title="Enter full path to and and including certificate file name">
+ <Col sm={4}>
+ <ControlLabel>Certificate File</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certFile"
+ className={error.certFile ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row title="Enter name/nickname of the certificate">
+ <Col sm={4}>
+ <ControlLabel>Certificate Nickname</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certName"
+ className={error.certName ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Add Certificate
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class SecurityAddCertModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ error
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Adding certificate...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Add Certificate
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>
+ Add certificate to the security database.
+ </h4>
+ <hr />
+ <Row title="Enter full path to and and including certificate file name">
+ <Col sm={4}>
+ <ControlLabel>Certificate File</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certFile"
+ className={error.certFile ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row title="Enter name/nickname of the certificate">
+ <Col sm={4}>
+ <ControlLabel>Certificate Nickname</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certName"
+ className={error.certName ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Add Certificate
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class SecurityEnableModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ primaryName,
+ certs,
+ spinning
+ } = this.props;
+
+ // Build list of cert names for the select list
+ let certNames = [];
+ for (let cert of certs) {
+ certNames.push(cert.attrs['nickname']);
+ }
+ let certNameOptions = certNames.map((name) =>
+ <option key={name} value={name}>{name}</option>
+ );
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Enabling security...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Enable Security
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>
+ You are choosing to enable security for the Directory Server which
+ allows the server to accept incoming client TLS connections. Please
+ select which certificate the server should use.
+ </h4>
+ <hr />
+ <Row className="ds-margin-top" title="The server certificate the Directory Server will use">
+ <Col sm={4}>
+ <ControlLabel>Available Certificates</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <select id="certNameSelect" onChange={handleChange} defaultValue={primaryName}>
+ {certNameOptions}
+ </select>
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Enable Security
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class EditCertModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ flags,
+ spinning
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Saving certificate...
+ </div>
+ </Row>;
+ }
+
+ // Process the cert flags
+ let CSSLChecked = false;
+ let CEmailChecked = false;
+ let COSChecked = false;
+ let TSSLChecked = false;
+ let TEmailChecked = false;
+ let TOSChecked = false;
+ let cSSLChecked = false;
+ let cEmailChecked = false;
+ let cOSChecked = false;
+ let PSSLChecked = false;
+ let PEmailChecked = false;
+ let POSChecked = false;
+ let pSSLChecked = false;
+ let pEmailChecked = false;
+ let pOSChecked = false;
+ let uSSLChecked = false;
+ let uEmailChecked = false;
+ let uOSChecked = false;
+ let SSLFlags = '';
+ let EmailFlags = '';
+ let OSFlags = '';
+ if (flags != "") {
+ [SSLFlags, EmailFlags, OSFlags] = flags.split(',');
+ if (SSLFlags.includes('T')) {
+ TSSLChecked = true;
+ }
+ if (EmailFlags.includes('T')) {
+ TEmailChecked = true;
+ }
+ if (OSFlags.includes('T')) {
+ TOSChecked = true;
+ }
+ if (SSLFlags.includes('C')) {
+ CSSLChecked = true;
+ }
+ if (EmailFlags.includes('C')) {
+ CEmailChecked = true;
+ }
+ if (OSFlags.includes('C')) {
+ COSChecked = true;
+ }
+ if (SSLFlags.includes('c')) {
+ cSSLChecked = true;
+ }
+ if (EmailFlags.includes('c')) {
+ cEmailChecked = true;
+ }
+ if (OSFlags.includes('c')) {
+ cOSChecked = true;
+ }
+ if (SSLFlags.includes('P')) {
+ PSSLChecked = true;
+ }
+ if (EmailFlags.includes('P')) {
+ PEmailChecked = true;
+ }
+ if (OSFlags.includes('P')) {
+ POSChecked = true;
+ }
+ if (SSLFlags.includes('p')) {
+ pSSLChecked = true;
+ }
+ if (EmailFlags.includes('p')) {
+ pEmailChecked = true;
+ }
+ if (OSFlags.includes('p')) {
+ pOSChecked = true;
+ }
+ if (SSLFlags.includes('u')) {
+ uSSLChecked = true;
+ }
+ if (EmailFlags.includes('u')) {
+ uEmailChecked = true;
+ }
+ if (OSFlags.includes('u')) {
+ uOSChecked = true;
+ }
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Edit Certificate Trust Flags
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Flags</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <ControlLabel>SSL</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <ControlLabel>Email</ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Object Signing</ControlLabel>
+ </Col>
+ </Row>
+ <hr />
+ <Row>
+ <Col sm={4} title="Trusted CA (flag 'C', also implies 'c' flag)">
+ (C) - Trusted CA
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="CflagSSL"
+ checked={CSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="CflagEmail"
+ checked={CEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="CflagOS"
+ checked={COSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Trusted CA for client authentication (flag 'T')">
+ (T) - Trusted CA Client Auth
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="TflagSSL"
+ checked={TSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="TflagEmail"
+ checked={TEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="TflagOS"
+ checked={TOSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Valid CA (flag 'c')">
+ (c) - Valid CA
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="cflagSSL"
+ checked={cSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="cflagEmail"
+ checked={cEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="cflagOS"
+ checked={cOSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Trusted Peer (flag 'P', implies flag 'p')">
+ (P) - Trusted Peer
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="PflagSSL"
+ checked={PSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="PflagEmail"
+ checked={PEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="PflagOS"
+ checked={POSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Valid Peer (flag 'p')">
+ (p) - Valid Peer
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="pflagSSL"
+ checked={pSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="pflagEmail"
+ checked={pEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="pflagOS"
+ checked={pOSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <hr />
+ <Row>
+ <Col sm={4} title="A private key is associated with the certificate. This is a dynamic flag and you cannot adjust it.">
+ (u) - Private Key
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="uflagSSL"
+ checked={uSSLChecked}
+ disabled
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="uflagEmail"
+ checked={uEmailChecked}
+ disabled
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="uflagOS"
+ checked={uOSChecked}
+ disabled
+ />
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Save
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+SecurityEnableModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ primaryName: PropTypes.string,
+ certs: PropTypes.array,
+ spinning: PropTypes.bool,
+};
+
+SecurityEnableModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ primaryName: "",
+ certs: [],
+ spinning: false,
+};
+
+EditCertModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ flags: PropTypes.string,
+ spinning: PropTypes.bool,
+};
+
+EditCertModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ flags: "",
+ spinning: false,
+};
+
+SecurityAddCertModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+SecurityAddCertModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ error: {},
+};
+
+SecurityAddCACertModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+SecurityAddCACertModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ error: {},
+};
diff --git a/src/cockpit/389-console/src/lib/security/securityTables.jsx b/src/cockpit/389-console/src/lib/security/securityTables.jsx
new file mode 100644
index 0000000..6b74a01
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/securityTables.jsx
@@ -0,0 +1,454 @@
+import React from "react";
+import {
+ // Button,
+ DropdownButton,
+ MenuItem,
+ actionHeaderCellFormatter,
+ sortableHeaderCellFormatter,
+ tableCellFormatter,
+ noop
+} from "patternfly-react";
+import { DSTable } from "../dsTable.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+
+class CertTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "nickname",
+ columns: [
+ {
+ property: "nickname",
+ header: {
+ label: "Certificate Name",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "subject",
+ header: {
+ label: "Subject DN",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "issuer",
+ header: {
+ label: "Issued By",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "flags",
+ header: {
+ label: "Trust Flags",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "expires",
+ header: {
+ label: "Expiration Date",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "action",
+ header: {
+ label: "",
+ props: {
+ index: 5,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 5
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.nickname[0]}>
+ <DropdownButton id={rowData.nickname[0]}
+ bsStyle="default" title="Actions">
+ <MenuItem eventKey="1" onClick={() => {
+ this.props.editCert(rowData);
+ }}
+ >
+ Edit Trust Flags
+ </MenuItem>
+ <MenuItem divider />
+ <MenuItem eventKey="2" onClick={() => {
+ this.props.delCert(rowData);
+ }}
+ >
+ Delete Certificate
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Certificates",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ let certRows = [];
+ let serverTable;
+ for (let cert of this.props.certs) {
+ let obj = {
+ 'nickname': [cert.attrs['nickname']],
+ 'subject': [cert.attrs['subject']],
+ 'issuer': [cert.attrs['issuer']],
+ 'expires': [cert.attrs['expires']],
+ 'flags': [cert.attrs['flags']],
+ };
+ certRows.push(obj);
+ }
+
+ if (certRows.length == 0) {
+ serverTable = <DSTable
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No Certificates"}]}
+ key={"nocerts"}
+ />;
+ } else {
+ serverTable = <DSTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={certRows}
+ key={certRows}
+ disableLoadingSpinner
+ />;
+ }
+
+ return (
+ <div>
+ {serverTable}
+ </div>
+ );
+ }
+}
+
+// Future - https://pagure.io/389-ds-base/issue/50491
+class CRLTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "name",
+ columns: [
+ {
+ property: "name",
+ header: {
+ label: "Issued By",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "effective",
+ header: {
+ label: "Effective Date",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "nextUpdate",
+ header: {
+ label: "Next Updateo",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+
+ {
+ property: "type",
+ header: {
+ label: "Type",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "action",
+ header: {
+ label: "",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.name[0]}>
+ <DropdownButton id={rowData.name[0]}
+ bsStyle="default" title="Actions">
+ <MenuItem eventKey="1" onClick={() => {
+ this.props.editIndex(rowData);
+ }}
+ >
+ View CRL
+ </MenuItem>
+ <MenuItem eventKey="2" onClick={() => {
+ this.props.reindexIndex(rowData);
+ }}
+ >
+ Delete CRL
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Certificate Revocation Lists",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ let crlTable;
+ if (this.props.rows.length == 0) {
+ crlTable = <DSTable
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "None"}]}
+ />;
+ } else {
+ crlTable = <DSTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={this.props.rows}
+ disableLoadingSpinner
+ />;
+ }
+ return (
+ <div>
+ {crlTable}
+ </div>
+ );
+ }
+}
+
+// Props and defaults
+
+CertTable.propTypes = {
+ // serverId: PropTypes.string,
+ certs: PropTypes.array,
+ editCert: PropTypes.func,
+ delCert: PropTypes.func,
+};
+
+CertTable.defaultProps = {
+ // serverId: "",
+ certs: [],
+ editCert: noop,
+ delCert: noop,
+};
+
+export {
+ CertTable,
+ CRLTable
+};
diff --git a/src/cockpit/389-console/src/lib/tools.jsx b/src/cockpit/389-console/src/lib/tools.jsx
index 5d482ba..b3e7573 100644
--- a/src/cockpit/389-console/src/lib/tools.jsx
+++ b/src/cockpit/389-console/src/lib/tools.jsx
@@ -1,8 +1,8 @@
export function searchFilter(searchFilterValue, columnsToSearch, rows) {
if (searchFilterValue && rows && rows.length) {
- const filteredRows = [];
+ let filteredRows = [];
rows.forEach(row => {
- var rowToSearch = [];
+ let rowToSearch = [];
if (columnsToSearch && columnsToSearch.length) {
columnsToSearch.forEach(column =>
rowToSearch.push(row[column])
@@ -27,18 +27,18 @@ export function searchFilter(searchFilterValue, columnsToSearch, rows) {
export function log_cmd(js_func, desc, cmd_array) {
if (console) {
- var pw_args = ["--passwd", "--bind-pw"];
- var cmd_list = [];
- var converted_pw = false;
+ let pw_args = ["--passwd", "--bind-pw"];
+ let cmd_list = [];
+ let converted_pw = false;
- for (var idx in cmd_array) {
- var cmd = cmd_array[idx];
+ for (let idx in cmd_array) {
+ let cmd = cmd_array[idx];
converted_pw = false;
for (var arg_idx in pw_args) {
if (cmd.startsWith(pw_args[arg_idx])) {
// We are setting a password, if it has a value we need to hide it
- var arg_len = cmd.indexOf("=");
- var arg = cmd.substring(0, arg_len);
+ let arg_len = cmd.indexOf("=");
+ let arg = cmd.substring(0, arg_len);
if (cmd.length != arg_len + 1) {
// We are setting a password value...
cmd_list.push(arg + "=**********");
diff --git a/src/cockpit/389-console/src/security.html b/src/cockpit/389-console/src/security.html
deleted file mode 100644
index 1444418..0000000
--- a/src/cockpit/389-console/src/security.html
+++ /dev/null
@@ -1,502 +0,0 @@
-
- <div id="sec-config" class="security-ctrl all-pages" hidden>
- <h3 class="ds-config-header">Security Configuration</h3>
-
- <input type="checkbox" class="ds-config-checkbox" id="nsslapd-security"><label
- for="nsslapd-security" class="ds-label" title="Enable security in the server (nsslapd-security)."> Enable Security</label>
- <hr class="">
- <div class="ds-expired-div" id="cert-attrs">
-
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="nsslapd-secureport" class="ds-config-label" title="The server's secure port number (nsslapd-secureport).">Server Secure Port</label><input
- class="ds-input" type="text" id="nsslapd-secureport" size="20"/>
- </div>
- <div>
- <label for="nsslapd-securelistenhost" class="ds-config-label" title="This parameter can be used to restrict the Directory Server instance to a single IP interface (hostname, or IP address). This parameter specifically sets what interface to use for TLS traffic. Requires restart. (nsslapd-securelistenhost).">
- Secure Listen Host Address</label><input class="ds-input" type="text" id="nsslapd-securelistenhost" size="20"/>
- </div>
- <div>
- <label for="sec-sslmin" class="ds-config-label" title="The minimum SSL/TLS version the server will accept (sslversionmin).">Minimum SSL/TLS Version </label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-sslmin">
- <option>TLS1.3</option>
- <option>TLS1.2</option>
- <option>TLS1.1</option>
- <option>TLS1.0</option>
- <option>SSL3</option>
- </select>
- </div>
- <div>
- <label for="sec-sslmax" class="ds-config-label" title="The maximum SSL/TLS version the server will accept (sslversionmax)."> Maximum SSL/TLS Version</label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-sslmax">
- <option>TLS1.3</option>
- <option>TLS1.2</option>
- <option>TLS1.1</option>
- <option>TLS1.0</option>
- <option>SSL3</option>
- </select>
- </div>
- <div>
- <label for="sec-clientauth" class="ds-config-label" title="shows how the Directory Server enforces client authentication (nsSSLClientAuth)."> Client Authentication</label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-clientauth">
- <option>Off</option>
- <option>Allowed</option>
- <option>Required</option>
- </select>
- </div>
- <div>
- <label for="sec-validate" class="ds-config-label" title="Validate server's certificate expiration date (nsslapd-validate-cert)."> Validate Certificate Expiration</label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-validate">
- <option>Warn</option>
- <option>On</option>
- <option>Off</option>
- </select>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
- <div class="ds-line">
- <div>
- <input type="checkbox" class="ds-config-checkbox" id="nsslapd-require-secure-binds"><label
- for="nsslapd-require-secure-binds" class="ds-label" title="Require all connections use TLS (nsslapd-require-secure-binds)."> Require Secure Connections</label>
- </div>
- <div>
- <input type="checkbox" class="ds-config-checkbox" id="nsslapd-ssl-check-hostname"><label
- for="nsslapd-ssl-check-hostname" class="ds-label" title="Verify authenticity of a request by matching the host name against the value assigned to the common name (cn) attribute of the subject name (subjectDN field) in the certificate being presented. (nsslapd-ssl-check-hostname)."> Verify Certificate Subject Hostname</label>
- </div>
- <div>
- <input type="checkbox" class="ds-config-checkbox" id="allowWeakCipher"><label
- for="allowWeakCipher" class="ds-label" title="Allow weak ciphers (allowWeakCipher)."> Allow Weak Ciphers</label>
- </div>
- <div class="ds-first">
- <button class="btn btn-default" id="set-sec-passwd-btn" title="Change the Security Database password">Set Security Password</button>
- </div>
- </div>
- </div>
- <hr>
-
- <h4>Allowed Ciphers</h4>
- <div class="ds-indent">
- <input type="checkbox" class="ds-config-checkbox" id="cipher-default-state"><label
- for="cipher-default-state" class="ds-label" title="Use the preferred default ciphers, as opposed to allowing all the ciphers">Use Default Ciphers</label>
- </div>
- <div id="cipher-table">
- <table class="table table-striped table-bordered table-hover ds-loglevel-table" id="allowed-cipher-table">
- <thead>
- <tr>
- <th class="ds-table-btn">State</th>
- <th>Cipher</th>
- </tr>
- </thead>
- <tbody>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown" id="cipher-all-state">
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td id="cipher-all">All</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown">
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown">
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_128_GCM_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_AES_128_GCM_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_AES_256_GCM_SHA384</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_AES_128_GCM_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_SEED_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_AES_256_CBC_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_AES_128_CBC_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_RC4_128_SHA</td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div class="ds-footer">
- <button class="btn btn-primary save-button">Save</button>
- </div>
- </div>
-
-
- <div class="security-ctrl all-pages" id="sec-ciphers-page" hidden>
- <h3 class="ds-config-header">Supported Ciphers</h3>
- <table id="nssslsupportedciphers" class="display ds-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Cipher Name</th>
- <th>Symmetric Cipher Name</th>
- <th>Mac Algorithm Name</th>
- <th>Symmetric Key Bits</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>TLS_RSA_WITH_AES_256_CBC_SHA256</td>
- <td>AES</td>
- <td>SHA256</td>
- <td>256</td>
- </tr>
- <tr>
- <td>TLS_DHE_DSS_WITH_DES_CBC_SHA</td>
- <td>AES</td>
- <td>SHA256</td>
- <td>256</td>
- </tr>
- </tbody>
- </table>
- </div>
-
- <div id="sec-cacert-page" class="all-pages" hidden>
- <h3 class="ds-config-header">CA Certificates</h3>
- <table id="ca-cert-table" class="display ds-repl-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>CA Certificate Name</th>
- <th>Trust Attributes</th>
- <th>Expiration Date</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>CA Certificate</td>
- <td>CTu,u,u</td>
- <td>2020/12/31</td>
- <td>
- <div class="dropdown">
- <button class="btn btn-default dropdown-toggle ds-agmt-dropdown-button" type="button" id="menu1" data-toggle="dropdown">Choose Action...
- <span class="caret"></span></button>
- <ul id="cert-dropdown" class="dropdown-menu ds-agmt-dropdown" role="menu" aria-labelledby="menu1">
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">View Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Edit Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Verify Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Export Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Delete Certificate</a></li>
- </ul>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- <button class="btn btn-primary" id="import-ca-cert" data-toggle="modal" data-target="#import-cacert-form">Import CA Certificate</button>
- </div>
-
- <div id="sec-svrcert-page" class="all-pages" hidden>
- <h3 class="ds-config-header">Server Certificates</h3>
- <table id="server-cert-table" class="display ds-repl-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Server Certificate Name</th>
- <th>Trust Attributes</th>
- <th>Issued To</th>
- <th>Issued By</th>
- <th>Expiration Date</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>Server-Cert</td>
- <td>u,u,Pu</td>
- <td>localhost.localdomain</td>
- <td>Mark's CA Cert</td>
- <td>2020/11/22</td>
- <td>
- <div class="dropdown">
- <button class="btn btn-default dropdown-toggle" type="button" id="menu1" data-toggle="dropdown">Choose Action...
- <span class="caret"></span></button>
- <ul id="cert-dropdown" class="dropdown-menu" role="menu" aria-labelledby="menu1">
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">View Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Verify Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Renew Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Export Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Delete Certificate</a></li>
- </ul>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- <button class="btn btn-default ds-spacing-sm" id="import-server-cert" data-toggle="modal" data-target="#import-cert-form">Import Certificate</button>
- <button class="btn btn-default" id="import-server-cert">Request Certificate</button>
- </div>
-
- <div id="sec-revoked-page" class="all-pages" hidden>
- <h3 class="ds-config-header">Revoked Certificates</h3>
- <table id="revoked-cert-table" class="display ds-repl-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Issued By</th>
- <th>Effective Date</th>
- <th>Next Update</th>
- <th>Type</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>Mark's CA Cert2</td>
- <td>2018/11/22</td>
- <td>2019/11/22</td>
- <td>CRL</td>
- <td>
- <div class="dropdown">
- <button class="btn btn-default dropdown-toggle ds-agmt-dropdown-button" type="button" id="menu1" data-toggle="dropdown">Choose Action...
- <span class="caret"></span></button>
- <ul id="cert-dropdown" class="dropdown-menu ds-agmt-dropdown" role="menu" aria-labelledby="menu1">
- <li role="certificate"><a role="menuitem" class="revoked-cert-dropdown" href="#">View</a></li>
- <li role="certificate"><a role="menuitem" class="revoked-cert-dropdown" href="#">Delete</a></li>
- </ul>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- <button class="btn btn-default ds-spacing-sm" id="add-revoked-btn" data-toggle="modal" data-target="#revoked-form">Add CRL/CKL</button>
- </div>
-
- <!-- Modals/Popups/Wizards -->
-
-
-
- <div class="modal fade" id="import-cert-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="import-cert-label" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title" id="import-cert-label">Import Server Certificate</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-inline">
- <label for="import-cert-file" class="" title="The name of the database link.">Certificate File</label><input
- class="ds-input ds-left-margin" type="text" id="import-cert-file" name="name" size="40">
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" class="btn btn-primary" id="import-cert-btn" data-dismiss="modal">Import Certificate</button>
- </div>
- </div>
- </div>
- </div>
-
- <div class="modal fade" data-backdrop="static" id="import-cacert-form" tabindex="-1" role="dialog" aria-labelledby="import-cacert-label" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title" id="import-cacert-label">Import CA Certificate</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-inline">
- <label for="import-cert-file" class="" title="The name of the database link.">Certificate File</label><input
- class="ds-input ds-left-margin" type="text" id="import-cacert-file" name="name" size="40">
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" class="btn btn-primary" id="import-cacert-btn" data-dismiss="modal">Import Certificate</button>
- </div>
- </div>
- </div>
- </div>
-
-
- <div class="modal fade" id="revoked-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="revoked-label" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title" id="revoked-label">Add Certificate Revocation List/Compromised Key List</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-inline">
- <label for="revoked-file">CRL/CKL PEM File</label><input
- class="ds-input ds-left-margin" type="text" id="revoked-file" name="name" size="40">
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" class="btn btn-primary" id="add-crl-btn" data-dismiss="modal">Add</button>
- </div>
- </div>
- </div>
- </div>
-
-
-
-
- <div id="export-cert" class="modal">
- <form class="modal-content animate">
- <div class="container">
- <h3 id=""><b>Export Certificate</b> <span class="close" id="export-cert-close">×</span></h3>
-
- <div class="clearfix ds-container">
- <div class="ds-panel-left">
- <button type="button" id="export-cert-cancel" class="ds-button-left">Cancel</button>
- </div>
- <div class="ds-panel-right">
- <button type="submit" id="export-cert-save" class="ds-button-right">Export Certificate</button>
- </div>
- </div>
- </div>
- </form>
- </div>
-
-
diff --git a/src/cockpit/389-console/src/security.js b/src/cockpit/389-console/src/security.js
deleted file mode 100644
index fd76dbb..0000000
--- a/src/cockpit/389-console/src/security.js
+++ /dev/null
@@ -1,137 +0,0 @@
-
-
-// TODO clear form functions
-
-
-$(document).ready( function() {
- $("#security-content").load("security.html", function () {
- // default setting
- $('#cert-attrs *').attr('disabled', true);
-
- $(".dropdown").on("change", function() {
- // Refreshes dropdown on Chrome
- $(this).blur();
- });
-
- $("#sec-config-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-config").show();
- });
-
- $("#sec-cacert-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-cacert-page").show();
- });
-
- $("#sec-srvcert-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-svrcert-page").show();
- });
- $("#sec-revoked-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-revoked-page").show();
- });
- $("#sec-ciphers-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-ciphers-page").show();
- });
-
- $("#sec-config").show();
-
- // Clear forms as theyare clicked
-
- $("#add-revoked-btn").on('click', function () {
- // TODO Clear form
-
- });
-
- $("#add-crl-btn").on('click', function () {
- // Add CRL/CKL
-
- // Close form
- $("#revoked-form").modal("toggle");
- });
-
- $('#nssslsupportedciphers').DataTable( {
- "paging": true,
- "bAutoWidth": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No ciphers!"
- }
- });
-
- $("#nsslapd-security").change(function() {
- if(this.checked) {
- $('#cert-attrs *').attr('disabled', false);
- } else {
- $('#cert-attrs *').attr('disabled', true);
- }
- });
-
- $("#cipher-default-state").change(function() {
- if(this.checked) {
- $("#cipher-table").hide();
- } else {
- $("#cipher-table").show();
- }
- });
-
- // Set up ca cert table
- $('#ca-cert-table').DataTable( {
- "paging": false,
- "bAutoWidth": false,
- "searching": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No Certificates In Database"
- },
- "columnDefs": [ {
- "targets": 3,
- "orderable": false
- } ]
- });
-
- // Set up server cert table
- $('#server-cert-table').DataTable( {
- "paging": false,
- "bAutoWidth": false,
- "searching": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No Certificates In Database"
- },
- "columnDefs": [ {
- "targets": 5,
- "orderable": false
- } ]
- });
-
- // Set up revoked cert table
- $('#revoked-cert-table').DataTable( {
- "paging": false,
- "bAutoWidth": false,
- "searching": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No Certificates In Database"
- },
- "columnDefs": [ {
- "targets": 4,
- "orderable": false
- } ]
- });
- // Page is loaded, mark it as so...
- security_page_loaded = 1;
- });
-});
-
diff --git a/src/cockpit/389-console/src/security.jsx b/src/cockpit/389-console/src/security.jsx
new file mode 100644
index 0000000..43edf49
--- /dev/null
+++ b/src/cockpit/389-console/src/security.jsx
@@ -0,0 +1,853 @@
+import cockpit from "cockpit";
+import React from "react";
+import Switch from "react-switch";
+import { NotificationController, ConfirmPopup } from "./lib/notifications.jsx";
+import { log_cmd } from "./lib/tools.jsx";
+import { Typeahead } from "react-bootstrap-typeahead";
+import { CertificateManagement } from "./lib/security/certificateManagement.jsx";
+import { SecurityEnableModal } from "./lib/security/securityModals.jsx";
+import { Ciphers } from "./lib/security/ciphers.jsx";
+import {
+ Nav,
+ NavItem,
+ TabContainer,
+ TabContent,
+ TabPane,
+ Col,
+ Row,
+ ControlLabel,
+ Button,
+ Checkbox,
+ Spinner
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import "./css/ds.css";
+
+export class Security extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ loaded: false,
+ saving: false,
+ notifications: [],
+ activeKey: 1,
+
+ errObj: {},
+ showConfirmDisable: false,
+ showSecurityEnableModal: false,
+ primaryCertName: '',
+ serverCertNames: [],
+ serverCerts: [],
+ // Ciphers
+ supportedCiphers: [],
+ enabledCiphers: [],
+ // Config settings
+ securityEnabled: false,
+ requireSecureBinds: false,
+ secureListenhost: false,
+ securePort: '636',
+ clientAuth: false,
+ checkHostname: false,
+ validateCert: '',
+ sslVersionMin: '',
+ sslVersionMax: '',
+ allowWeakCipher: false,
+ nssslpersonalityssl: '',
+ // Original config Settings
+ _securityEnabled: false,
+ _requireSecureBinds: false,
+ _secureListenhost: false,
+ _securePort: '636',
+ _clientAuth: false,
+ _checkHostname: false,
+ _validateCert: '',
+ _sslVersionMin: '',
+ _sslVersionMax: '',
+ _allowWeakCipher: false,
+ _nssslpersonalityssl: '',
+ };
+
+ this.handleChange = this.handleChange.bind(this);
+ this.addNotification = this.addNotification.bind(this);
+ this.removeNotification = this.removeNotification.bind(this);
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ this.handleSwitchChange = this.handleSwitchChange.bind(this);
+ this.handleTypeaheadChange = this.handleTypeaheadChange.bind(this);
+ this.loadSecurityConfig = this.loadSecurityConfig.bind(this);
+ this.loadEnabledCiphers = this.loadEnabledCiphers.bind(this);
+ this.loadSupportedCiphers = this.loadSupportedCiphers.bind(this);
+ this.loadCerts = this.loadCerts.bind(this);
+ this.loadCACerts = this.loadCACerts.bind(this);
+ this.closeConfirmDisable = this.closeConfirmDisable.bind(this);
+ this.enableSecurity = this.enableSecurity.bind(this);
+ this.disableSecurity = this.disableSecurity.bind(this);
+ this.saveSecurityConfig = this.saveSecurityConfig.bind(this);
+ this.closeSecurityEnableModal = this.closeSecurityEnableModal.bind(this);
+ }
+
+ addNotification(type, message, timerdelay, persistent) {
+ this.setState(prevState => ({
+ notifications: [
+ ...prevState.notifications,
+ {
+ key: prevState.notifications.length + 1,
+ type: type,
+ persistent: persistent,
+ timerdelay: timerdelay,
+ message: message,
+ }
+ ]
+ }));
+ }
+
+ removeNotification(notificationToRemove) {
+ this.setState({
+ notifications: this.state.notifications.filter(
+ notification => notificationToRemove.key !== notification.key
+ )
+ });
+ }
+
+ componentWillMount () {
+ if (!this.state.loaded) {
+ this.setState({securityEnabled: true}, this.setState({securityEnabled: false}));
+ this.loadSecurityConfig();
+ }
+ }
+
+ loadSupportedCiphers () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ciphers", "list", "--supported"
+ ];
+ log_cmd("loadSupportedCiphers", "Load the security configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ this.setState({
+ supportedCiphers: config.items
+ }, this.loadEnabledCiphers);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security configuration - ${msg}`
+ );
+ });
+ }
+
+ loadEnabledCiphers () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ciphers", "list", "--enabled"
+ ];
+ log_cmd("loadEnabledCiphers", "Load the security configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ this.setState({
+ enabledCiphers: config.items,
+ }, this.loadCerts);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security configuration - ${msg}`
+ );
+ });
+ }
+
+ loadCACerts () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ca-certificate", "list",
+ ];
+ log_cmd("loadCACerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let certs = JSON.parse(content);
+ this.setState(() => (
+ {
+ CACerts: certs,
+ loaded: true
+ })
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading CA certificates - ${msg}`
+ );
+ });
+ }
+
+ loadCerts () {
+ // Set loaded: true
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "list",
+ ];
+ log_cmd("loadCerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const certs = JSON.parse(content);
+ let certNames = [];
+ for (let cert of certs) {
+ certNames.push(cert.attrs['nickname']);
+ }
+ this.setState(() => (
+ {
+ serverCerts: certs,
+ serverCertNames: certNames,
+ }), this.loadCACerts
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading server certificates - ${msg}`
+ );
+ });
+ }
+
+ loadRSAConfig() {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "rsa", "get"
+ ];
+ log_cmd("loadRSAConfig", "Load the RSA configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ const nickname = config.items['nssslpersonalityssl'];
+ this.setState(() => (
+ {
+ nssslpersonalityssl: nickname,
+ _nssslpersonalityssl: nickname,
+ }
+ ), this.loadSupportedCiphers);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security RSA configuration - ${msg}`
+ );
+ });
+ }
+
+ loadSecurityConfig(saving) {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "get"
+ ];
+ log_cmd("loadSecurityConfig", "Load the security configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ const attrs = config.items;
+ let secEnabled = false;
+ let secReqSecBinds = false;
+ let clientAuth = "allowed";
+ let validateCert = "warn";
+ let cipherPref = "default";
+ let allowWeak = false;
+
+ if ('nsslapd-security' in attrs) {
+ if (attrs['nsslapd-security'].toLowerCase() == "on") {
+ secEnabled = true;
+ }
+ }
+ if ('nsslapd-require-secure-binds' in attrs) {
+ if (attrs['nsslapd-require-secure-binds'].toLowerCase() == "on") {
+ secReqSecBinds = true;
+ }
+ }
+ if ('nssslclientauth' in attrs) {
+ if (attrs['nssslclientauth'] != "") {
+ clientAuth = attrs['nssslclientauth'];
+ }
+ }
+ if ('nsslapd-validate-cert' in attrs) {
+ if (attrs['nsslapd-validate-cert'] != "") {
+ validateCert = attrs['nsslapd-validate-cert'].toLowerCase();
+ }
+ }
+ if ('allowweakcipher' in attrs) {
+ if (attrs['allowweakcipher'].toLowerCase() == "on") {
+ allowWeak = true;
+ }
+ }
+ if ('nsssl3ciphers' in attrs) {
+ if (attrs['nsssl3ciphers'] != "") {
+ cipherPref = attrs['nsssl3ciphers'];
+ }
+ }
+
+ this.setState(() => (
+ {
+ securityEnabled: secEnabled,
+ requireSecureBinds: secReqSecBinds,
+ secureListenhost: attrs['nsslapd-securelistenhost'],
+ securePort: attrs['nsslapd-secureport'],
+ clientAuth: clientAuth,
+ checkHostname: attrs['nsslapd-ssl-check-hostname'],
+ validateCert: validateCert,
+ sslVersionMin: attrs['sslversionmin'],
+ sslVersionMax: attrs['sslversionmax'],
+ allowWeakCipher: allowWeak,
+ cipherPref: cipherPref,
+ _securityEnabled: secEnabled,
+ _requireSecureBinds: secReqSecBinds,
+ _secureListenhost: attrs['nsslapd-securelistenhost'],
+ _securePort: attrs['nsslapd-secureport'],
+ _clientAuth: clientAuth,
+ _checkHostname: attrs['nsslapd-ssl-check-hostname'],
+ _validateCert: validateCert,
+ _sslVersionMin: attrs['sslversionmin'],
+ _sslVersionMax: attrs['sslversionmax'],
+ _allowWeakCipher: allowWeak,
+ }
+ ), function() {
+ if (!saving) {
+ this.loadRSAConfig();
+ }
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security configuration - ${msg}`
+ );
+ });
+ }
+
+ handleNavSelect(key) {
+ this.setState({
+ activeKey: key
+ });
+ }
+
+ handleSwitchChange(value) {
+ if (!value) {
+ // We are disabling security, ask for confirmation
+ this.setState({showConfirmDisable: true});
+ } else {
+ // Check if we have certs, if we do make the user choose one from dropdown list, otherwise reject the
+ // enablement
+ if (this.state.serverCerts.length > 0) {
+ this.setState({
+ primaryCertName: this.state.nssslpersonalityssl,
+ showSecurityEnableModal: true,
+ });
+ } else {
+ this.addNotification(
+ "error",
+ `There must be at least one server certificate present in the security database to enable security`
+ );
+ }
+ }
+ }
+
+ closeSecurityEnableModal () {
+ this.setState({
+ showSecurityEnableModal: false,
+ });
+ }
+
+ handleSecEnableChange (e) {
+ const value = e.target.value.trim();
+ this.setState({
+ primaryCertName: value,
+ });
+ }
+
+ closeConfirmDisable () {
+ this.setState({
+ showConfirmDisable: false
+ });
+ }
+
+ enableSecurity () {
+ /* start the spinner */
+ this.setState({
+ secEnableSpinner: true
+ });
+
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "enable",
+ ];
+ log_cmd("enableSecurity", "Enable security", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.addNotification(
+ "success",
+ `Successfully enabled security. You must restart the server for this to take effect.`
+ );
+ this.setState({
+ securityEnabled: true,
+ secEnableSpinner: false,
+ showSecurityEnableModal: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error enabling security - ${msg}`
+ );
+ this.setState({
+ secEnableSpinner: false,
+ showSecurityEnableModal: false,
+ });
+ });
+ }
+
+ disableSecurity () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "disable",
+ ];
+ log_cmd("disableSecurity", "Disable security", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.addNotification(
+ "success",
+ `Successfully disabled security. You must restart the server for this to take effect.`
+ );
+ this.setState({
+ securityEnabled: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error disabling security - ${msg}`
+ );
+ });
+ }
+
+ saveSecurityConfig () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'security', 'set'
+ ];
+
+ if (this.state._validateCert != this.state.validateCert) {
+ cmd.push("--verify-cert-chain-on-startup=" + this.state.validateCert);
+ }
+ if (this.state._sslVersionMin != this.state.sslVersionMin) {
+ cmd.push("--tls-protocol-min=" + this.state.sslVersionMin);
+ }
+ if (this.state._sslVersionMax != this.state.sslVersionMax) {
+ cmd.push("--tls-protocol-max=" + this.state.sslVersionMax);
+ }
+ if (this.state._clientAuth != this.state.clientAuth) {
+ cmd.push("--tls-client-auth=" + this.state.clientAuth);
+ }
+ if (this.state._securePort != this.state.securePort) {
+ cmd.push("--secure-port=" + this.state.securePort);
+ }
+ if (this.state._secureListenhost != this.state.secureListenhost) {
+ cmd.push("--listen-host=" + this.state.secureListenhost);
+ }
+ if (this.state._allowWeakCipher != this.state.allowWeakCipher) {
+ let val = "off";
+ if (this.state.allowWeakCipher) {
+ val = "on";
+ }
+ cmd.push("--allow-insecure-ciphers=" + val);
+ }
+ if (this.state._checkHostname != this.state.checkHostname) {
+ let val = "off";
+ if (this.state.checkHostname) {
+ val = "on";
+ }
+ cmd.push("--check-hostname=" + val);
+ }
+ if (this.state._requireSecureBinds != this.state.requireSecureBinds) {
+ let val = "off";
+ if (this.state.requireSecureBinds) {
+ val = "on";
+ }
+ cmd.push("--require-secure-authentication=" + val);
+ }
+
+ if (cmd.length > 5) {
+ log_cmd("saveSecurityConfig", "Applying security config change", cmd);
+ let msg = "Successfully updated security configuration. You must restart the server for these changes to take effect.";
+
+ this.setState({
+ // Start the spinner
+ saving: true
+ });
+
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.loadSecurityConfig(1);
+ this.addNotification(
+ "success",
+ msg
+ );
+ this.setState({
+ saving: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.loadSecurityConfig();
+ this.setState({
+ saving: false
+ });
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error updating security configuration - ${msg}`
+ );
+ });
+ }
+ }
+
+ handleTypeaheadChange(value) {
+ if (value.length == 0) {
+ return;
+ }
+ this.setState({
+ nssslpersonalityssl: value[0],
+ });
+ }
+
+ handleChange(e) {
+ const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
+ this.setState({
+ [e.target.id]: value,
+ });
+ }
+
+ handleLoginModal(e) {
+ const value = e.target.value.trim();
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ render() {
+ let securityPage = "";
+ let serverCert = [this.state.nssslpersonalityssl];
+
+ if (this.state.loaded && !this.state.saving) {
+ let configPage = "";
+ if (this.state.securityEnabled) {
+ configPage =
+ <div>
+ <Row className="ds-margin-top" title="The server's secure port number (nsslapd-secureport).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Server Secure Port
+ </Col>
+ <Col sm={4}>
+ <input id="securePort" className="ds-input-auto" onChange={this.handleChange} type="text" defaultValue={this.state.securePort} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="This parameter can be used to restrict the Directory Server instance to a single IP interface (hostname, or IP address). This parameter specifically sets what interface to use for TLS traffic. Requires restart. (nsslapd-securelistenhost).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Secure Listen Host
+ </Col>
+ <Col sm={4}>
+ <input id="secureListenhost" className="ds-input-auto" type="text" onChange={this.handleChange} defaultValue={this.state.secureListenhost} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The name, or nickname, of the server certificate inthe NSS datgabase the server should use (nsSSLPersonalitySSL).">
+ <Col className="ds-no-padding" sm={2}>
+ <ControlLabel>Server Certificate Name</ControlLabel>
+ </Col>
+ <Col sm={4}>
+ <Typeahead
+ id="serverCertNameTypeahead"
+ onChange={this.handleTypeaheadChange}
+ selected={serverCert}
+ emptyLabel="No matching certificates found"
+ options={this.state.serverCertNames}
+ newSelectionPrefix="Select a server certificate"
+ placeholder="Type a sever certificate nickname..."
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The minimum SSL/TLS version the server will accept (sslversionmin).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Minimum TLS Version
+ </Col>
+ <Col sm={4}>
+ <select id="sslVersionMin" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.sslVersionMin}>
+ <option />
+ <option>TLS1.3</option>
+ <option>TLS1.2</option>
+ <option>TLS1.1</option>
+ <option>TLS1.0</option>
+ <option>SSL3</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The maximum SSL/TLS version the server will accept (sslversionmax).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Maximum TLS Version
+ </Col>
+ <Col sm={4}>
+ <select id="sslVersionMax" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.sslVersionMax}>
+ <option />
+ <option>TLS1.3</option>
+ <option>TLS1.2</option>
+ <option>TLS1.1</option>
+ <option>TLS1.0</option>
+ <option>SSL3</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Sets how the Directory Server enforces TLS client authentication (nsSSLClientAuth).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Client Authentication
+ </Col>
+ <Col sm={4}>
+ <select id="clientAuth" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.clientAuth}>
+ <option>off</option>
+ <option>allowed</option>
+ <option>required</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Validate server's certificate expiration date (nsslapd-validate-cert).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Validate Certificate
+ </Col>
+ <Col sm={4}>
+ <select id="validateCert" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.validateCert}>
+ <option>warn</option>
+ <option>on</option>
+ <option>off</option>
+ </select>
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={5}>
+ <Checkbox
+ id="requireSecureBinds"
+ defaultChecked={this.state.requireSecureBinds}
+ onChange={this.handleChange}
+ title="Require all connections use TLS (nsslapd-require-secure-binds)."
+ >
+ Require Secure Connections
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={5}>
+ <Checkbox
+ id="checkHostname"
+ defaultChecked={this.state.checkHostname}
+ onChange={this.handleChange}
+ title="Verify authenticity of a request by matching the host name against the value assigned to the common name (cn) attribute of the subject name (subjectDN field) in the certificate being presented. (nsslapd-ssl-check-hostname)."
+ >
+ Verify Certificate Subject Hostname
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={5}>
+ <Checkbox
+ id="allowWeakCipher"
+ defaultChecked={this.state.allowWeakCipher}
+ onChange={this.handleChange}
+ title="Allow weak ciphers (allowWeakCipher)."
+ >
+ Allow Weak Ciphers
+ </Checkbox>
+ </Col>
+ </Row>
+ <p />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top-med"
+ onClick={() => {
+ this.saveSecurityConfig();
+ }}
+ >
+ Save Configuration
+ </Button>
+ </div>;
+ }
+
+ securityPage =
+ <div className="container-fluid">
+ <NotificationController
+ notifications={this.state.notifications}
+ removeNotificationAction={this.removeNotification}
+ />
+ <div className="ds-tab-table">
+ <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: 'Security Configuration'}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: 'Certificate Management'}} />
+ </NavItem>
+ <NavItem eventKey={3}>
+ <div dangerouslySetInnerHTML={{__html: 'Cipher Preferences'}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-xlg ds-indent">
+ <Row>
+ <Col componentClass={ControlLabel} sm={2}>
+ Security Enabled
+ </Col>
+ <Col sm={2}>
+ <Switch
+ onChange={this.handleSwitchChange}
+ checked={this.state.securityEnabled}
+ height={20}
+ />
+ </Col>
+ </Row>
+ <hr />
+ {configPage}
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <CertificateManagement
+ serverId={this.props.serverId}
+ CACerts={this.state.CACerts}
+ ServerCerts={this.state.serverCerts}
+ addNotification={this.addNotification}
+ />
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={3}>
+ <div className="ds-indent ds-tab-table">
+ <Ciphers
+ serverId={this.props.serverId}
+ supportedCiphers={this.state.supportedCiphers}
+ cipherPref={this.state.cipherPref}
+ enabledCiphers={this.state.enabledCiphers}
+ addNotification={this.addNotification}
+ />
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ </div>;
+ } else if (this.state.saving) {
+ securityPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Saving security information ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ securityPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading security information ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ }
+ return (
+ <div>
+ {securityPage}
+ <ConfirmPopup
+ showModal={this.state.showConfirmDisable}
+ closeHandler={this.closeConfirmDisable}
+ actionFunc={this.disableSecurity}
+ msg="Are you sure you want to disable security?"
+ msgContent="Attention: this requires the server to be restarted to take effect."
+ />
+ <SecurityEnableModal
+ showModal={this.state.showSecurityEnableModal}
+ closeHandler={this.closeSecurityEnableModal}
+ handleChange={this.handleSecEnableChange}
+ saveHandler={this.enableSecurity}
+ primaryName={this.state.primaryCertName}
+ certs={this.state.serverCerts}
+ spinning={this.state.secEnableSpinner}
+ />
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+Security.propTypes = {
+ serverId: PropTypes.string,
+};
+
+Security.defaultProps = {
+ serverId: "",
+};
+
+export default Security;
diff --git a/src/cockpit/389-console/webpack.config.js b/src/cockpit/389-console/webpack.config.js
index 8c0b433..941a6e8 100644
--- a/src/cockpit/389-console/webpack.config.js
+++ b/src/cockpit/389-console/webpack.config.js
@@ -34,8 +34,6 @@ var info = {
"replication.js",
"schema.html",
"schema.js",
- "security.html",
- "security.js",
"servers.html",
"servers.js",
"static",
@@ -131,7 +129,18 @@ module.exports = {
{
exclude: /node_modules/,
loader: "babel-loader",
- test: /\.jsx$/
+ test: /\.jsx$/,
+ options: {
+ presets: [
+ '@babel/preset-env',
+ '@babel/preset-react',
+ {
+ plugins: [
+ '@babel/plugin-proposal-class-properties'
+ ]
+ }
+ ]
+ },
},
{
exclude: /node_modules/,
diff --git a/src/lib389/cli/dsconf b/src/lib389/cli/dsconf
index c0c0b4d..22635ca 100755
--- a/src/lib389/cli/dsconf
+++ b/src/lib389/cli/dsconf
@@ -11,14 +11,11 @@
# PYTHON_ARGCOMPLETE_OK
import argparse, argcomplete
-import logging
import ldap
import sys
import signal
import json
import ast
-from lib389 import DirSrv
-from lib389._constants import DN_CONFIG, DN_DM
from lib389.cli_conf import config as cli_config
from lib389.cli_conf import backend as cli_backend
from lib389.cli_conf import directory_manager as cli_directory_manager
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
index c7324f1..6e2e54a 100644
--- a/src/lib389/lib389/__init__.py
+++ b/src/lib389/lib389/__init__.py
@@ -1,5 +1,5 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2019 Red Hat, Inc.
# Copyright (C) 2019 William Brown <william(a)blackhats.net.au>
# All rights reserved.
#
@@ -19,40 +19,28 @@
TODO: reorganize method parameters according to SimpleLDAPObject
naming: filterstr, attrlist
"""
-try:
- from subprocess import Popen, PIPE, STDOUT
- HASPOPEN = True
-except ImportError:
- import popen2
- HASPOPEN = False
-
-import io
+
import sys
import os
import stat
import pwd
import grp
import os.path
-import base64
import socket
import ldif
import re
import ldap
import ldapurl
import time
-import operator
import shutil
from datetime import datetime
import logging
-import decimal
import glob
import tarfile
import subprocess
from collections.abc import Callable
import signal
import errno
-import pwd
-import grp
import uuid
import json
from shutil import copy2
@@ -63,25 +51,18 @@ import warnings
import inspect
from ldap.ldapobject import SimpleLDAPObject
-from ldap.cidict import cidict
-from ldap import LDAPError
# file in this package
from lib389._constants import *
from lib389.properties import *
from lib389._entry import Entry
-from lib389._replication import CSN, RUV
from lib389._ldifconn import LDIFConn
from lib389.tools import DirSrvTools
-from lib389.mit_krb5 import MitKrb5
from lib389.utils import (
ds_is_older,
isLocalHost,
- is_a_dn,
normalizeDN,
- suffixfilt,
escapeDNValue,
- update_newhost_with_fqdn,
formatInfData,
ensure_bytes,
ensure_str,
@@ -765,7 +746,7 @@ class DirSrv(SimpleLDAPObject, object):
for pi in potential_inst:
pi_dse_ldif = os.path.join(pi, 'dse.ldif')
# Takes /etc/dirsrv/slapd-instance -> slapd-instance -> instance
- pi_name = pi.split('/')[-1].split('-')[-1]
+ pi_name = pi.split('/')[-1].split('slapd-')[-1]
# parse + append
if os.path.exists(pi_dse_ldif):
instances.append(_parse_configfile(pi_dse_ldif, pi_name))
@@ -3094,7 +3075,7 @@ class DirSrv(SimpleLDAPObject, object):
]
try:
- result = subprocess.check_output(cmd, encoding='utf-8')
+ subprocess.check_output(cmd, encoding='utf-8')
except subprocess.CalledProcessError as e:
self.log.debug("Command: %s failed with the return code %s and the error %s",
format_cmd_list(cmd), e.returncode, e.output)
diff --git a/src/lib389/lib389/cli_conf/security.py b/src/lib389/lib389/cli_conf/security.py
index 6d8c1ae..20f2574 100644
--- a/src/lib389/lib389/cli_conf/security.py
+++ b/src/lib389/lib389/cli_conf/security.py
@@ -8,7 +8,7 @@
from collections import OrderedDict, namedtuple
import json
-
+import os
from lib389.config import Config, Encryption, RSA
from lib389.nss_ssl import NssSsl
@@ -27,45 +27,48 @@ SECURITY_ATTRS_MAP = OrderedDict([
('secure-port', Props(Config, 'nsslapd-securePort',
'Port for LDAPS to listen on',
range(1, 65536))),
- ('tls-client-auth', Props(Config, 'nsSSLClientAuth',
- 'Client authentication requirement',
- ('off', 'allowed', 'required'))),
+ ('tls-client-auth', Props(Encryption, 'nsSSLClientAuth',
+ 'Client authentication requirement',
+ ('off', 'allowed', 'required'))),
('require-secure-authentication', Props(Config, 'nsslapd-require-secure-binds',
- 'Require binds over LDAPS, StartTLS, or SASL',
- onoff)),
+ 'Require binds over LDAPS, StartTLS, or SASL',
+ onoff)),
('check-hostname', Props(Config, 'nsslapd-ssl-check-hostname',
'Check Subject of remote certificate against the hostname',
onoff)),
('verify-cert-chain-on-startup', Props(Config, 'nsslapd-validate-cert',
- 'Validate server certificate during startup',
- ('warn', *onoff))),
+ 'Validate server certificate during startup',
+ ('warn', *onoff))),
('session-timeout', Props(Encryption, 'nsSSLSessionTimeout',
'Secure session timeout',
int)),
('tls-protocol-min', Props(Encryption, 'sslVersionMin',
- 'Secure protocol minimal allowed version',
- protocol_versions)),
+ 'Secure protocol minimal allowed version',
+ protocol_versions)),
('tls-protocol-max', Props(Encryption, 'sslVersionMax',
- 'Secure protocol maximal allowed version',
- protocol_versions)),
+ 'Secure protocol maximal allowed version',
+ protocol_versions)),
('allow-insecure-ciphers', Props(Encryption, 'allowWeakCipher',
- 'Allow weak ciphers for legacy use',
- onoff)),
+ 'Allow weak ciphers for legacy use',
+ onoff)),
('allow-weak-dh-param', Props(Encryption, 'allowWeakDHParam',
'Allow short DH params for legacy use',
onoff)),
+ ('cipher-pref', Props(Encryption, 'nsSSL3Ciphers',
+ 'Use this command to directly set nsSSL3Ciphers attribute. It is a comma separated list '
+ 'of cipher names (prefixed with + or -), optionally including +all or -all. The attribute '
+ 'may optionally be prefixed by keyword default. Please refer to documentation of '
+ 'the attribute for a more detailed description.',
+ onoff)),
])
RSA_ATTRS_MAP = OrderedDict([
('tls-allow-rsa-certificates', Props(RSA, 'nsSSLActivation',
- 'Activate use of RSA certificates',
- onoff)),
+ 'Activate use of RSA certificates', onoff)),
('nss-cert-name', Props(RSA, 'nsSSLPersonalitySSL',
- 'Server certificate name in NSS DB',
- str)),
+ 'Server certificate name in NSS DB', str)),
('nss-token', Props(RSA, 'nsSSLToken',
- 'Security token name (module of NSS DB)',
- str))
+ 'Security token name (module of NSS DB)', str))
])
@@ -73,7 +76,9 @@ def _security_generic_get(inst, basedn, logs, args, attrs_map):
result = {}
for attr, props in attrs_map.items():
val = props.cls(inst).get_attr_val_utf8(props.attr)
- result[props.attr] = val
+ if val is None:
+ val = ""
+ result[props.attr.lower()] = val
if args.json:
print(json.dumps({'type': 'list', 'items': result}))
else:
@@ -126,14 +131,22 @@ def _security_generic_toggle_parsers(parent, cls, attr, help_pattern):
return list(map(add_parser, ('Enable', 'Disable'), ('on', 'off')))
-
def security_enable(inst, basedn, log, args):
dbpath = inst.get_cert_dir()
tlsdb = NssSsl(dbpath=dbpath)
- if not tlsdb._db_exists(even_partial=True): # we want to be very careful
- log.info(f'Secure database does not exist. Creating a new one in {dbpath}.')
- tlsdb.reinit()
-
+ certs = tlsdb.list_certs()
+ if len(certs) == 0:
+ raise ValueError('There are no server certificates in the security ' +
+ 'database, security can not be enabled.')
+
+ if len(certs) == 1:
+ # If there is only cert make sure it is set as the server certificate
+ RSA(inst).set('nsSSLPersonalitySSL', certs[0][0])
+ elif args.cert_name is not None:
+ # A certificate nickname was provided, set it as the server certificate
+ RSA(inst).set('nsSSLPersonalitySSL', args.cert_name)
+
+ # it should now be safe to enable security
Config(inst).set('nsslapd-security', 'on')
@@ -184,29 +197,246 @@ def security_ciphers_list(inst, basedn, log, args):
print(*lst, sep='\n')
+def cert_add(inst, basedn, log, args):
+ """Add server certificate
+ """
+ # Verify file and certificate name
+ os.path.isfile(args.file)
+ tlsdb = NssSsl(dirsrv=inst)
+ if not tlsdb._db_exists(even_partial=True): # we want to be very careful
+ log.info('Security database does not exist. Creating a new one in {}.'.format(inst.get_cert_dir()))
+ tlsdb.reinit()
+
+ try:
+ tlsdb.get_cert_details(args.name)
+ raise ValueError("Certificate already exists with the same name")
+ except ValueError:
+ pass
+
+ if args.primary_cert:
+ # This is the server's primary certificate, update RSA entry
+ RSA(inst).set('nsSSLPersonalitySSL', args.name)
+
+ # Add the cert
+ tlsdb.add_cert(args.name, args.file)
+
+
+def cacert_add(inst, basedn, log, args):
+ """Add CA certificate
+ """
+ # Verify file and certificate name
+ os.path.isfile(args.file)
+ tlsdb = NssSsl(dirsrv=inst)
+ if not tlsdb._db_exists(even_partial=True): # we want to be very careful
+ log.info('Security database does not exist. Creating a new one in {}.'.format(inst.get_cert_dir()))
+ tlsdb.reinit()
+
+ try:
+ tlsdb.get_cert_details(args.name)
+ raise ValueError("Certificate already exists with the same name")
+ except ValueError:
+ pass
+
+ # Add the cert
+ tlsdb.add_cert(args.name, args.file, ca=True)
+
+
+def cert_list(inst, basedn, log, args):
+ """List all the server certificates
+ """
+ cert_list = []
+ tlsdb = NssSsl(dirsrv=inst)
+ certs = tlsdb.list_certs()
+ for cert in certs:
+ if args.json:
+ cert_list.append(
+ {
+ "type": "certificate",
+ "attrs": {
+ 'nickname': cert[0],
+ 'subject': cert[1],
+ 'issuer': cert[2],
+ 'expires': cert[3],
+ 'flags': cert[4],
+ }
+ }
+ )
+ else:
+ log.info('Certificate Name: {}'.format(cert[0]))
+ log.info('Subject DN: {}'.format(cert[1]))
+ log.info('Issuer DN: {}'.format(cert[2]))
+ log.info('Expires: {}'.format(cert[3]))
+ log.info('Trust Flags: {}\n'.format(cert[4]))
+ if args.json:
+ log.info(json.dumps(cert_list))
+
+
+def cacert_list(inst, basedn, log, args):
+ """List all CA certs
+ """
+ cert_list = []
+ tlsdb = NssSsl(dirsrv=inst)
+ certs = tlsdb.list_certs(ca=True)
+ for cert in certs:
+ if args.json:
+ cert_list.append(
+ {
+ "type": "certificate",
+ "attrs": {
+ 'nickname': cert[0],
+ 'subject': cert[1],
+ 'issuer': cert[2],
+ 'expires': cert[3],
+ 'flags': cert[4],
+ }
+ }
+ )
+ else:
+ log.info('Certificate Name: {}'.format(cert[0]))
+ log.info('Subject DN: {}'.format(cert[1]))
+ log.info('Issuer DN: {}'.format(cert[2]))
+ log.info('Expires: {}'.format(cert[3]))
+ log.info('Trust Flags: {}\n'.format(cert[4]))
+ if args.json:
+ log.info(json.dumps(cert_list))
+
+
+def cert_get(inst, basedn, log, args):
+ """Get the details about a server certificate
+ """
+ tlsdb = NssSsl(dirsrv=inst)
+ details = tlsdb.get_cert_details(args.name)
+ if args.json:
+ log.info(json.dumps(
+ {
+ "type": "certificate",
+ "attrs": {
+ 'nickname': details[0],
+ 'subject': details[1],
+ 'issuer': details[2],
+ 'expires': details[3],
+ 'flags': details[4],
+ }
+ }
+ )
+ )
+ else:
+ log.info('Certificate Name: {}'.format(details[0]))
+ log.info('Subject DN: {}'.format(details[1]))
+ log.info('Issuer DN: {}'.format(details[2]))
+ log.info('Expires: {}'.format(details[3]))
+ log.info('Trust Flags: {}'.format(details[4]))
+
+
+def cert_edit(inst, basedn, log, args):
+ """Edit cert
+ """
+ tlsdb = NssSsl(dirsrv=inst)
+ tlsdb.edit_cert_trust(args.name, args.flags)
+
+
+def cert_del(inst, basedn, log, args):
+ """Delete cert
+ """
+ tlsdb = NssSsl(dirsrv=inst)
+ tlsdb.del_cert(args.name)
+
+
def create_parser(subparsers):
security = subparsers.add_parser('security', help='Query and manipulate security options')
security_sub = security.add_subparsers(help='security')
- security_set = _security_generic_set_parser(security_sub, SECURITY_ATTRS_MAP, 'Set general security options',
+
+ # Core security management
+ _security_generic_set_parser(security_sub, SECURITY_ATTRS_MAP, 'Set general security options',
('Use this command for setting security related options located in cn=config and cn=encryption,cn=config.'
'\n\nTo enable/disable security you can use enable and disable commands instead.'))
- security_get = _security_generic_get_parser(security_sub, SECURITY_ATTRS_MAP, 'Get general security options')
+ _security_generic_get_parser(security_sub, SECURITY_ATTRS_MAP, 'Get general security options')
security_enable_p = security_sub.add_parser('enable', help='Enable security', description=(
'If missing, create security database, then turn on security functionality. Please note this is usually not'
- ' enought for TLS connections to work - proper setup of CA and server certificate is necessary.'))
+ ' enough for TLS connections to work - proper setup of CA and server certificate is necessary.'))
+ security_enable_p.add_argument('--cert-name', default=None,
+ help='The name of the certificate the server should use')
security_enable_p.set_defaults(func=security_enable)
security_disable_p = security_sub.add_parser('disable', help='Disable security', description=(
'Turn off security functionality. The rest of the configuration will be left untouched.'))
security_disable_p.set_defaults(func=security_disable)
- rsa = security_sub.add_parser('rsa', help='Query and mainpulate RSA security options')
+ # Server certificate management
+ certs = security_sub.add_parser('certificate', help='Manage TLS certificates')
+ certs_sub = certs.add_subparsers(help='certificate')
+ cert_add_parser = certs_sub.add_parser('add', help='Add a server certificate', description=(
+ 'Add a server certificate to the NSS database'))
+ cert_add_parser.add_argument('--file', required=True,
+ help='The file name of the certificate')
+ cert_add_parser.add_argument('--name', required=True,
+ help='The name/nickname of the certificate')
+ cert_add_parser.add_argument('--primary-cert', action='store_true',
+ help="Set this certificate as the server's certificate")
+ cert_add_parser.set_defaults(func=cert_add)
+
+ cert_edit_parser = certs_sub.add_parser('set-trust-flags', help='Set the Trust flags',
+ description=('Change the trust flags of a server certificate'))
+ cert_edit_parser.add_argument('name', help='The name/nickname of the certificate')
+ cert_edit_parser.add_argument('--flags', required=True,
+ help='The trust flags for the server certificate')
+ cert_edit_parser.set_defaults(func=cert_edit)
+
+ cert_del_parser = certs_sub.add_parser('del', help='Delete a certificate',
+ description=('Delete a certificate from the NSS database'))
+ cert_del_parser.add_argument('name', help='The name/nickname of the certificate')
+ cert_del_parser.set_defaults(func=cert_del)
+
+ cert_get_parser = certs_sub.add_parser('get', help="Get a server certificate's information",
+ description=('Get detailed information about a certificate, like trust attributes, expiration dates, Subject and Issuer DNs '))
+ cert_get_parser.add_argument('name', help='The name/nickname of the certificate')
+ cert_get_parser.set_defaults(func=cert_get)
+
+ cert_list_parser = certs_sub.add_parser('list', help='List the server certificates',
+ description=('List the server certificates in the NSS database'))
+ cert_list_parser.set_defaults(func=cert_list)
+
+ # CA certificate management
+ cacerts = security_sub.add_parser('ca-certificate', help='Manage TLS Certificate Authorities')
+ cacerts_sub = cacerts.add_subparsers(help='ca-certificate')
+ cacert_add_parser = cacerts_sub.add_parser('add', help='Add a Certificate Authority', description=(
+ 'Add a Certificate Authority to the NSS database'))
+ cacert_add_parser.add_argument('--file', required=True,
+ help='The file name of the CA certificate')
+ cacert_add_parser.add_argument('--name', required=True,
+ help='The name/nickname of the CA certificate')
+ cacert_add_parser.set_defaults(func=cacert_add)
+
+ cacert_edit_parser = cacerts_sub.add_parser('set-trust-flags', help='Set the Trust flags',
+ description=('Change the trust attributes of a CA certificate. Certificate Authorities typically use "CT,,"'))
+ cacert_edit_parser.add_argument('name', help='The name/nickname of the CA certificate')
+ cacert_edit_parser.add_argument('--flags', required=True,
+ help='The trust flags for the CA certificate')
+ cacert_edit_parser.set_defaults(func=cert_edit)
+
+ cacert_del_parser = cacerts_sub.add_parser('del', help='Delete a certificate',
+ description=('Delete a CA certificate from the NSS database'))
+ cacert_del_parser.add_argument('name', help='The name/nickname of the CA certificate')
+ cacert_del_parser.set_defaults(func=cert_del)
+
+ cacert_get_parser = cacerts_sub.add_parser('get', help="Get a Certificate Authority's information",
+ description=('Get detailed information about a CA certificate, like trust attributes, expiration dates, Subject and Issuer DN'))
+ cacert_get_parser.add_argument('name', help='The name/nickname of the CA certificate')
+ cacert_get_parser.set_defaults(func=cert_get)
+
+ cacert_list_parser = cacerts_sub.add_parser('list', help='List the Certificate Authorities',
+ description=('List the CA certificates in the NSS database'))
+ cacert_list_parser.set_defaults(func=cacert_list)
+
+ # RSA management
+ rsa = security_sub.add_parser('rsa', help='Query and manipulate RSA security options')
rsa_sub = rsa.add_subparsers(help='rsa')
- rsa_set = _security_generic_set_parser(rsa_sub, RSA_ATTRS_MAP, 'Set RSA security options',
+ _security_generic_set_parser(rsa_sub, RSA_ATTRS_MAP, 'Set RSA security options',
('Use this command for setting RSA (private key) related options located in cn=RSA,cn=encryption,cn=config.'
'\n\nTo enable/disable RSA you can use enable and disable commands instead.'))
- rsa_get = _security_generic_get_parser(rsa_sub, RSA_ATTRS_MAP, 'Get RSA security options')
- rsa_toggles = _security_generic_toggle_parsers(rsa_sub, RSA, 'nsSSLActivation', '{} RSA')
+ _security_generic_get_parser(rsa_sub, RSA_ATTRS_MAP, 'Get RSA security options')
+ _security_generic_toggle_parsers(rsa_sub, RSA, 'nsSSLActivation', '{} RSA')
+ # Cipher management
ciphers = security_sub.add_parser('ciphers', help='Manage secure ciphers')
ciphers_sub = ciphers.add_subparsers(help='ciphers')
@@ -226,7 +456,7 @@ def create_parser(subparsers):
ciphers_set = ciphers_sub.add_parser('set', help='Set ciphers attribute', description=(
'Use this command to directly set nsSSL3Ciphers attribute. It is a comma separated list '
- 'of cipher names (prefixed with + or -), optionaly including +all or -all. The attribute '
+ 'of cipher names (prefixed with + or -), optionally including +all or -all. The attribute '
'may optionally be prefixed by keyword default. Please refer to documentation of '
'the attribute for a more detailed description.'))
ciphers_set.set_defaults(func=security_ciphers_set)
diff --git a/src/lib389/lib389/config.py b/src/lib389/lib389/config.py
index c2a34fa..23ab9f2 100644
--- a/src/lib389/lib389/config.py
+++ b/src/lib389/lib389/config.py
@@ -20,9 +20,7 @@ import ldap
from lib389._constants import *
from lib389 import Entry
from lib389._mapped_object import DSLdapObject
-from lib389.dseldif import DSEldif
-from lib389.utils import ensure_bytes, ensure_str
-
+from lib389.utils import ensure_bytes, selinux_label_port, selinux_present
from lib389.lint import DSCLE0001, DSCLE0002, DSELE0001
class Config(DSLdapObject):
@@ -37,7 +35,7 @@ class Config(DSLdapObject):
super(Config, self).__init__(instance=conn)
self._dn = DN_CONFIG
# self._instance = conn
- # self.log = conn.log
+ self.log = conn.log
config_compare_exclude = [
'nsslapd-ldapifilepath',
'nsslapd-accesslog',
@@ -65,6 +63,16 @@ class Config(DSLdapObject):
def rdn(self):
return DN_CONFIG
+ def replace(self, key, value):
+ if key.lower() == 'nsslapd-secureport' and selinux_present():
+ # Get old port and remove label
+ old_port = self.get_attr_val_utf8(key)
+ self.log.debug("Removing old port's selinux label...")
+ selinux_label_port(old_port, remove_label=True)
+ self.log.debug("Setting new port's selinux label...")
+ selinux_label_port(value)
+ super(Config, self).replace(key, value)
+
def _alter_log_enabled(self, service, state):
if service not in ('access', 'error', 'audit'):
self._log.error('Attempted to enable invalid log service "%s"' % service)
@@ -245,7 +253,10 @@ class Encryption(DSLdapObject):
:returns: list of str
"""
val = self.get_attr_val_utf8('nsSSL3Ciphers')
- return val.split(',') if val else []
+ if val:
+ return val.split(',')
+ else:
+ return ['default']
@ciphers.setter
def ciphers(self, ciphers):
@@ -370,7 +381,7 @@ class CertmapLegacy(object):
def _parse_maps(self, maps):
certmaps = {}
- cur_map = None
+
for l in maps:
if l.startswith('certmap'):
# Line matches format of: certmap name issuer
@@ -457,10 +468,7 @@ class LDBMConfig(DSLdapObject):
def __init__(self, conn):
super(LDBMConfig, self).__init__(instance=conn)
self._dn = DN_CONFIG_LDBM
- config_compare_exclude = []
+ # config_compare_exclude = []
self._rdn_attribute = 'cn'
self._lint_functions = []
self._protected = True
-
-
-
diff --git a/src/lib389/lib389/nss_ssl.py b/src/lib389/lib389/nss_ssl.py
index a54095c..8af7132 100644
--- a/src/lib389/lib389/nss_ssl.py
+++ b/src/lib389/lib389/nss_ssl.py
@@ -10,9 +10,6 @@
"""
import os
-import sys
-import random
-import string
import re
import socket
import time
@@ -24,7 +21,7 @@ from datetime import datetime, timedelta
from subprocess import check_output
from lib389.passwd import password_generate
-from lib389.utils import ensure_str, ensure_bytes, format_cmd_list
+from lib389.utils import ensure_str, format_cmd_list
import uuid
KEYBITS = 4096
@@ -362,8 +359,9 @@ only.
# Now make the lines usable
cert_values = []
for line in lines:
- data = line.split()
- cert_values.append((data[0], data[1]))
+ if line == '':
+ continue
+ cert_values.append(re.match(r'^(.+[^\s])[\s]+([^\s]+)$', line.rstrip()).groups())
return cert_values
def _rsa_cert_key_exists(self, cert_tuple):
@@ -380,7 +378,6 @@ only.
result = ensure_str(check_output(cmd, stderr=subprocess.STDOUT))
lines = result.split('\n')[1:-1]
- key_list = []
for line in lines:
m = re.match('\<(?P<id>.*)\> (?P<type>\w+)\s+(?P<hash>\w+).*:(?P<name>.+)', line)
if name == m.group('name'):
@@ -712,3 +709,148 @@ only.
crt_der_path = '%s/%s%s.der' % (self._certdb, USER_PREFIX, name)
return {'ca': ca_path, 'key': key_path, 'crt': crt_path, 'crt_der_path': crt_der_path}
+ # Certificate helper functions
+ def del_cert(self, nickname):
+ """Delete this certificate
+ """
+ cmd = [
+ '/usr/bin/certutil',
+ '-D',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("del_cert cmd: %s", format_cmd_list(cmd))
+ check_output(cmd, stderr=subprocess.STDOUT)
+
+ def edit_cert_trust(self, nickname, trust_flags):
+ """Edit trust flags
+ """
+
+ # validate trust flags
+ flag_sections = trust_flags.split(',')
+ if len(flag_sections) != 3:
+ raise ValueError("Invalid trust flag format")
+
+ for section in flag_sections:
+ if len(section) > 6:
+ raise ValueError("Invalid trust flag format, too many flags in a section")
+
+ for c in trust_flags:
+ if c not in ['p', 'P', 'c', 'C', 'T', 'u', ',']:
+ raise ValueError("Invalid trust flag {}".format(c))
+
+ # Modify certificate flags
+ cmd = [
+ '/usr/bin/certutil',
+ '-M',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-t', trust_flags,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("edit_cert_trust cmd: %s", format_cmd_list(cmd))
+ check_output(cmd, stderr=subprocess.STDOUT)
+
+
+ def get_cert_details(self, nickname):
+ """Get the trust flags, subject DN, issuer, and expiration date
+
+ return a list:
+ 0 - nickname
+ 1 - subject
+ 2 - issuer
+ 3 - expire date
+ 4 - trust_flags
+ """
+ all_certs = self._rsa_cert_list()
+ for cert in all_certs:
+ if cert[0] == nickname:
+ trust_flags = cert[1]
+ cmd = [
+ '/usr/bin/certutil',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-L',
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("get_cert_details cmd: %s", format_cmd_list(cmd))
+
+ # Expiration date
+ certdetails = check_output(cmd, stderr=subprocess.STDOUT, encoding='utf-8')
+ end_date_str = certdetails.split("Not After : ")[1].split("\n")[0]
+ date_format = '%a %b %d %H:%M:%S %Y'
+ end_date = datetime.strptime(end_date_str, date_format)
+
+ # Subject DN
+ subject = ""
+ for line in certdetails.splitlines():
+ line = line.lstrip()
+ if line.startswith("Subject: "):
+ subject = line.split("Subject: ")[1].split("\n")[0]
+ elif subject != "":
+ if not line.startswith("Subject Public Key Info:"):
+ subject += line
+ else:
+ # Done, strip off quotes
+ subject = subject[1:-1]
+ break
+
+ # Issuer
+ issuer = ""
+ for line in certdetails.splitlines():
+ line = line.lstrip()
+ if line.startswith("Issuer: "):
+ issuer = line.split("Issuer: ")[1].split("\n")[0]
+ elif issuer != "":
+ if not line.startswith("Validity:"):
+ issuer += line
+ else:
+ issuer = issuer[1:-1]
+ break
+
+ return ([nickname, subject, issuer, str(end_date), trust_flags])
+
+ # Did not find cert with that name
+ raise ValueError("Certificate '{}' not found in NSS database".format(nickname))
+
+
+ def list_certs(self, ca=False):
+ all_certs = self._rsa_cert_list()
+ certs = []
+ for cert in all_certs:
+ trust_flags = cert[1]
+ if (ca and "CT" in trust_flags) or (not ca and "CT" not in trust_flags):
+ certs.append(self.get_cert_details(cert[0]))
+ return certs
+
+
+ def add_cert(self, nickname, input_file, ca=False):
+ """Add server or CA cert
+ """
+
+ # Verify input_file exists
+ if not os.path.exists(input_file):
+ raise ValueError("The certificate file ({}) does not exist".format(input_file))
+
+ if ca:
+ trust_flags = "CT,,"
+ else:
+ trust_flags = ",,"
+
+ cmd = [
+ '/usr/bin/certutil',
+ '-A',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-t', trust_flags,
+ '-i', input_file,
+ '-a',
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("add_cert cmd: %s", format_cmd_list(cmd))
+ check_output(cmd, stderr=subprocess.STDOUT)
diff --git a/src/lib389/lib389/utils.py b/src/lib389/lib389/utils.py
index 1472309..10c8cae 100644
--- a/src/lib389/lib389/utils.py
+++ b/src/lib389/lib389/utils.py
@@ -234,7 +234,7 @@ def selinux_label_port(port, remove_label=False):
"""
Either set or remove an SELinux label(ldap_port_t) for a TCP port
- :param port: The TCP port to be labelled
+ :param port: The TCP port to be labeled
:type port: str
:param remove_label: Set True if the port label should be removed
:type remove_label: boolean
@@ -258,9 +258,10 @@ def selinux_label_port(port, remove_label=False):
# We only label ports that ARE NOT in the default policy that comes with
# a RH based system.
+ port = int(port)
selinux_default_ports = [389, 636, 3268, 3269, 7389]
if port in selinux_default_ports:
- log.debug('port %s already in %s, skipping port relabel' % (port, selinux_default_ports))
+ log.debug('port {} already in {}, skipping port relabel'.format(port, selinux_default_ports))
return
label_set = False
@@ -283,11 +284,10 @@ def selinux_label_port(port, remove_label=False):
elif not remove_label:
# Port belongs to someone else (bad)
# This is only an issue during setting a label, not removing a label
- raise ValueError("Port {} was already labelled with: ({}) Please choose a different port number".format(port, policy['type']))
+ raise ValueError("Port {} was already labeled with: ({}) Please choose a different port number".format(port, policy['type']))
if (remove_label and label_set) or (not remove_label and not label_set):
for i in range(5):
-
try:
subprocess.check_call(["semanage", "port",
"-d" if remove_label else "-a",
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months
[389-ds-base] branch master updated: Issue 50325 - Add Security tab to UI
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch master
in repository 389-ds-base.
The following commit(s) were added to refs/heads/master by this push:
new a77abdb Issue 50325 - Add Security tab to UI
a77abdb is described below
commit a77abdbc4fc9fbc846d6827e28e0d3fb4eb81fe0
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Wed Jul 3 16:36:53 2019 -0400
Issue 50325 - Add Security tab to UI
Description: This updates the CLI and UI to handle a majority of
the security configuration. It also adds support
for PF dual list selection even though I ended up
not using it.
Relates: https://pagure.io/389-ds-base/issue/50325
Reviewed by: spichugi, and mhonek (Thanks!!)
Fixed Simon's issues
Fix issue with listing certs with spaces in the name
Fix npm vulnerabilities
Fix selinux port labeling, and add 'saving' spinners
Use a regex for parsing certutil output
---
src/cockpit/389-console/.babelrc | 5 +-
src/cockpit/389-console/package-lock.json | 117 +++
src/cockpit/389-console/package.json | 2 +
src/cockpit/389-console/src/css/ds.css | 186 ++++-
src/cockpit/389-console/src/ds.js | 6 +-
src/cockpit/389-console/src/index.es6 | 7 +
src/cockpit/389-console/src/index.html | 19 +-
.../src/lib/database/databaseTables.jsx | 7 +-
.../src/lib/security/certificateManagement.jsx | 617 +++++++++++++++
.../389-console/src/lib/security/ciphers.jsx | 274 +++++++
.../src/lib/security/securityModals.jsx | 689 +++++++++++++++++
.../src/lib/security/securityTables.jsx | 454 +++++++++++
src/cockpit/389-console/src/lib/tools.jsx | 18 +-
src/cockpit/389-console/src/security.html | 502 ------------
src/cockpit/389-console/src/security.js | 137 ----
src/cockpit/389-console/src/security.jsx | 853 +++++++++++++++++++++
src/cockpit/389-console/webpack.config.js | 15 +-
src/lib389/cli/dsconf | 3 -
src/lib389/lib389/__init__.py | 27 +-
src/lib389/lib389/cli_conf/security.py | 298 ++++++-
src/lib389/lib389/config.py | 28 +-
src/lib389/lib389/nss_ssl.py | 156 +++-
src/lib389/lib389/utils.py | 8 +-
23 files changed, 3674 insertions(+), 754 deletions(-)
diff --git a/src/cockpit/389-console/.babelrc b/src/cockpit/389-console/.babelrc
index d0ef093..23c75c5 100644
--- a/src/cockpit/389-console/.babelrc
+++ b/src/cockpit/389-console/.babelrc
@@ -1,4 +1,7 @@
{
"presets": ["@babel/env",
- "@babel/preset-react"]
+ "@babel/preset-react"],
+ "plugins": [
+ "@babel/plugin-proposal-class-properties"
+ ]
}
diff --git a/src/cockpit/389-console/package-lock.json b/src/cockpit/389-console/package-lock.json
index 67f170c..9fec71c 100644
--- a/src/cockpit/389-console/package-lock.json
+++ b/src/cockpit/389-console/package-lock.json
@@ -104,6 +104,105 @@
"@babel/types": "^7.0.0"
}
},
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/h...",
+ "integrity": "sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.4.4",
+ "@babel/helper-split-export-declaration": "^7.4.4"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz",
+ "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.4.4",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.11",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-...",
+ "integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/traverse": "^7.4.4",
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helpe...",
+ "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.4.4"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz",
+ "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==",
+ "dev": true
+ },
+ "@babel/traverse": {
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz",
+ "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.4.4",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.4.4",
+ "@babel/parser": "^7.4.5",
+ "@babel/types": "^7.4.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.11"
+ }
+ },
+ "@babel/types": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
+ "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
"@babel/helper-define-map": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7...",
@@ -336,6 +435,16 @@
"@babel/plugin-syntax-async-generators": "^7.0.0"
}
},
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plug...",
+ "integrity": "sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.4.4",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
"@babel/plugin-proposal-json-strings": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-p...",
@@ -7202,6 +7311,14 @@
"warning": "^3.0.0"
}
},
+ "react-switch": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-5.0.0.tgz",
+ "integrity": "sha512-+zxY9xj9dMc8Y4gv/kkqQrirfEiIQ+SlQfJDW1Wi81L3xoh1fcbBYyJyh0TnhM/U/b6HxuBmkmU4Ooxgtuoavw==",
+ "requires": {
+ "prop-types": "^15.6.2"
+ }
+ },
"react-transition-group": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-grou...",
diff --git a/src/cockpit/389-console/package.json b/src/cockpit/389-console/package.json
index 7d6fb4e..ddf44b5 100644
--- a/src/cockpit/389-console/package.json
+++ b/src/cockpit/389-console/package.json
@@ -19,6 +19,7 @@
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
+ "@babel/plugin-proposal-class-properties": "^7.0.0",
"ajv": "^6.0.0",
"audit-ci": "^1.7.0",
"babel-eslint": "^9.0.0",
@@ -57,6 +58,7 @@
"react-bootstrap": "0.32.4",
"react-bootstrap-typeahead": "3.2.4",
"react-dom": "16.6.1",
+ "react-switch": "^5.0.0",
"recompose": "0.30.0",
"table-resolver": "4.1.1"
}
diff --git a/src/cockpit/389-console/src/css/ds.css b/src/cockpit/389-console/src/css/ds.css
index 1ad8d5c..f8945bf 100644
--- a/src/cockpit/389-console/src/css/ds.css
+++ b/src/cockpit/389-console/src/css/ds.css
@@ -44,7 +44,7 @@
/* Main nav page index.html */
.ds-content {
padding: 0;
- padding-top: 115px;
+ padding-top: 115px; /* this pushes the content below fixed nav bar */
padding-bottom: 50px;
margin-top: 0;
margin-left: 25px;
@@ -672,6 +672,11 @@ td {
padding-bottom: 10px;
}
+.ds-cipher-width {
+ max-width: 350px !important;
+ min-width: 350px !important;
+}
+
/*
* Popup modal stuff
*/
@@ -1637,8 +1642,8 @@ option {
font-size: 16px;
}
-.ds-no-padding () {
- padding: 0 !imporant;
+.ds-no-padding {
+ padding-right: 0 !important;
}
.alert {
@@ -1650,6 +1655,10 @@ option {
margin-top: 5%;
}
+.ds-select {
+ width: 120px;
+}
+
.treeview .list-group-item {
/* remove focus border */
outline: none;
@@ -1680,3 +1689,174 @@ input {
.ds-width-auto {
width: 100%;
}
+
+/* Dual List CSS */
+.dual-list-pf-arrows {
+ display: inline-block;
+ margin: auto;
+ position: relative;
+ bottom: 170px;
+ font-size: 23px;
+ color: #bbb;
+}
+@media only screen and (max-width: 600px) {
+ .dual-list-pf-arrows {
+ display: block;
+ position: inherit;
+ margin: 5px 0;
+ padding-left: 79px;
+ }
+}
+
+.dual-list-pf-arrows span {
+ display: block;
+ margin: 25px;
+ cursor: pointer;
+ transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
+ transform: rotate(-90deg);
+}
+@media only screen and (max-width: 600px) {
+ .dual-list-pf-arrows span {
+ display: inline;
+ margin: 0 20px 0 0;
+ }
+}
+
+.dual-list-pf-arrows span:hover {
+ color: #8b8d8f;
+}
+
+.dual-list-pf-body {
+ height: 375px;
+ width: 320px;
+ overflow-y: scroll;
+ overflow-x: auto;
+ display: inline-grid;
+ align-content: flex-start;
+}
+
+.dual-list-pf-body::-webkit-scrollbar {
+ width: 12px;
+ height: 12px;
+ background: #fafafa;
+}
+
+.dual-list-pf-body::-webkit-scrollbar-thumb {
+ background: #d1d1d1;
+ border-radius: 6px;
+ border: 3px solid transparent;
+ background-clip: content-box;
+}
+
+.dual-list-pf-body::-webkit-scrollbar-thumb:hover {
+ background: #bbb;
+ border-radius: 6px;
+ border: 3px solid transparent;
+ background-clip: content-box;
+}
+
+.dual-list-pf-filter {
+ margin-left: 20px;
+}
+
+.dual-list-pf-filter input {
+ background-color: #f5f5f5;
+ border: 1px solid #ededed;
+ width: 145px;
+ padding: 0 22px 0 5px;
+ margin-top: 3px;
+ margin-bottom: 3px;
+}
+
+.dual-list-pf-filter .search-icon {
+ position: relative;
+ right: 20px;
+ bottom: 1px;
+ color: #bbb;
+}
+
+.dual-list-pf-filter ::-webkit-input-placeholder {
+ font-style: italic;
+}
+
+.dual-list-pf-footer {
+ padding: 10px;
+ border-top: 1px solid #d1d1d1;
+}
+
+.dual-list-pf-heading {
+ border-bottom: 1px solid #d1d1d1;
+}
+
+.dual-list-pf-item {
+ padding: 5px 0;
+ margin-bottom: 0;
+ font-weight: 400;
+ transition: background 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), color 0.3s ease-out;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.dual-list-pf-item input[type='checkbox'] {
+ position: relative;
+ left: 10px;
+ vertical-align: top;
+ cursor: pointer;
+}
+
+.dual-list-pf-item.selected {
+ background-color: #0088ce;
+ color: white;
+}
+
+.dual-list-pf-item.disabled {
+ cursor: not-allowed;
+ background: #f5f5f5;
+ color: #8b8d8f;
+}
+
+.dual-list-pf-item.disabled input[type='checkbox'] {
+ cursor: not-allowed;
+}
+
+.dual-list-pf-item.child {
+ padding-left: 22px;
+}
+
+.dual-list-pf-item:hover:not(.selected):not(.disabled) {
+ background-color: #bee1f4;
+ color: inherit;
+}
+
+.dual-list-pf-item-label {
+ margin-left: 20px;
+}
+
+.dual-list-pf-main-checkbox {
+ position: relative;
+ left: 10px;
+ vertical-align: text-top;
+ cursor: pointer;
+}
+
+.dual-list-pf-no-items {
+ margin-top: 30px;
+ text-align: center;
+}
+
+.dual-list-pf-selector {
+ display: inline-block;
+ border: 1px solid #d1d1d1;
+ user-select: none;
+}
+
+.dual-list-pf-sort-icon {
+ cursor: pointer;
+}
+
+.dropdown-kebab-pf.btn-group {
+ margin-left: 10px;
+ float: right;
+ margin-right: 10px;
+}
+/* End of dual list */
diff --git a/src/cockpit/389-console/src/ds.js b/src/cockpit/389-console/src/ds.js
index ec57488..e023fdc 100644
--- a/src/cockpit/389-console/src/ds.js
+++ b/src/cockpit/389-console/src/ds.js
@@ -8,7 +8,7 @@ var dn_regex = new RegExp( "^([A-Za-z]+=.*)" );
* to track the loading, and once all the pages are loaded, then we can load the config
*/
var server_page_loaded = 0;
-var security_page_loaded = 0;
+var security_page_loaded = 1;
var db_page_loaded = 1;
var repl_page_loaded = 0;
var plugin_page_loaded = 1;
@@ -482,4 +482,8 @@ $(window.document).ready(function() {
$(".all-pages").hide();
$("#monitor-content").show();
});
+ $("#security-tab").on("click", function() {
+ $(".all-pages").hide();
+ $("#security-content").show();
+ });
});
diff --git a/src/cockpit/389-console/src/index.es6 b/src/cockpit/389-console/src/index.es6
index 0483752..71e9c5e 100644
--- a/src/cockpit/389-console/src/index.es6
+++ b/src/cockpit/389-console/src/index.es6
@@ -3,6 +3,7 @@ import ReactDOM from "react-dom";
import { Plugins } from "./plugins.jsx";
import { Database } from "./database.jsx";
import { Monitor } from "./monitor.jsx";
+import { Security } from "./security.jsx";
var serverIdElem;
@@ -35,6 +36,12 @@ function renderReactDOM(clear) {
<Monitor serverId={serverIdElem} key={tabKey} />,
document.getElementById("monitor")
);
+
+ // Security tab
+ ReactDOM.render(
+ <Security serverId={serverIdElem} key={tabKey} />,
+ document.getElementById("security")
+ );
}
// We have to create the wrappers because this is no simple way
diff --git a/src/cockpit/389-console/src/index.html b/src/cockpit/389-console/src/index.html
index 7c5dbf8..c93f754 100644
--- a/src/cockpit/389-console/src/index.html
+++ b/src/cockpit/389-console/src/index.html
@@ -21,7 +21,6 @@
<script src="ds.js"></script>
<script src="schema.js"></script>
<script src="servers.js"></script>
- <script src="security.js"></script>
<script src="replication.js"></script>
<link href="static/bootstrap.min.css" rel="stylesheet">
<link href="static/jquery.dataTables.min.css" type="text/css" rel="stylesheet">
@@ -80,23 +79,10 @@
</li>
<!-- Security navtab -->
- <li class="dropdown ds-nav-tab">
- <a href="#0" class="dropdown-toggle ds-tab-list" data-toggle="dropdown" id="security-tab">
+ <li class="ds-nav-tab">
+ <a href="#0" class="ds-tab-list ds-tab-standalone" id="security-tab">
Security
- <b class="caret"></b>
</a>
- <ul class="dropdown-menu ds-nav-item">
- <li><a href="#0" class="ds-nav-choice" id="sec-config-btn" parent-id="security-tab">Security Settings</a></li>
- <li class="dropdown-submenu">
- <a tabindex="-1" href="#0">Certificate Management</a>
- <ul class="dropdown-menu">
- <li><a href="#0" class="ds-nav-choice" id="sec-cacert-btn" parent-id="security-tab">CA Certificates</a></li>
- <li><a href="#0" class="ds-nav-choice" id="sec-srvcert-btn" parent-id="security-tab">Server Certificates</a></li>
- <li><a href="#0" class="ds-nav-choice" id="sec-revoked-btn" parent-id="security-tab">Revoked Certificates</a></li>
- </ul>
- </li>
- <li><a href="#0" class="ds-nav-choice" id="sec-ciphers-btn" parent-id="security-tab">Supported Ciphers</a></li>
- </ul>
</li>
<!-- Database navtab -->
@@ -501,6 +487,7 @@
</div>
<div id="security-content" class="all-pages" hidden>
+ <div id="security"></div>
</div>
<div id="database-content" class="all-pages" hidden>
diff --git a/src/cockpit/389-console/src/lib/database/databaseTables.jsx b/src/cockpit/389-console/src/lib/database/databaseTables.jsx
index adf535e..5d90f02 100644
--- a/src/cockpit/389-console/src/lib/database/databaseTables.jsx
+++ b/src/cockpit/389-console/src/lib/database/databaseTables.jsx
@@ -53,7 +53,7 @@ class ReferralTable extends React.Component {
},
cell: {
props: {
- index: 2
+ index: 1
},
formatters: [
(value, { rowData }) => {
@@ -75,6 +75,7 @@ class ReferralTable extends React.Component {
]
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -345,6 +346,7 @@ class EncryptedAttrTable extends React.Component {
]
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -508,6 +510,7 @@ class LDIFTable extends React.Component {
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
}
getSingleColumn () {
@@ -703,6 +706,7 @@ class LDIFManageTable extends React.Component {
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
} // Constructor
getColumns() {
@@ -877,6 +881,7 @@ class BackupTable extends React.Component {
};
this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
} // Constructor
getColumns() {
diff --git a/src/cockpit/389-console/src/lib/security/certificateManagement.jsx b/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
new file mode 100644
index 0000000..d5cd927
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/certificateManagement.jsx
@@ -0,0 +1,617 @@
+import cockpit from "cockpit";
+import React from "react";
+import {
+ Nav,
+ NavItem,
+ TabContainer,
+ TabContent,
+ TabPane,
+ Button,
+ Spinner,
+ noop
+} from "patternfly-react";
+import { ConfirmPopup } from "../../lib/notifications.jsx";
+import {
+ CertTable
+} from "./securityTables.jsx";
+import {
+ EditCertModal,
+ SecurityAddCertModal,
+ SecurityAddCACertModal,
+} from "./securityModals.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { log_cmd } from "../../lib/tools.jsx";
+
+export class CertificateManagement extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeKey: 1,
+ ServerCerts: this.props.ServerCerts,
+ CACerts: this.props.CACerts,
+ showEditModal: false,
+ showAddModal: false,
+ modalSpinner: false,
+ showConfirmDelete: false,
+ certName: "",
+ certFile: "",
+ flags: "",
+ errObj: {},
+ isCACert: false,
+ showConfirmCAChange: false,
+ loading: false,
+ };
+
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ this.addCACert = this.addCACert.bind(this);
+ this.handleAddChange = this.handleAddChange.bind(this);
+ this.addCert = this.addCert.bind(this);
+ this.showAddModal = this.showAddModal.bind(this);
+ this.closeAddModal = this.closeAddModal.bind(this);
+ this.showAddCAModal = this.showAddCAModal.bind(this);
+ this.closeAddCAModal = this.closeAddCAModal.bind(this);
+ this.showEditModal = this.showEditModal.bind(this);
+ this.closeEditModal = this.closeEditModal.bind(this);
+ this.showEditCAModal = this.showEditCAModal.bind(this);
+ this.handleFlagChange = this.handleFlagChange.bind(this);
+ this.editCert = this.editCert.bind(this);
+ this.doEditCert = this.doEditCert.bind(this);
+ this.closeConfirmCAChange = this.closeConfirmCAChange.bind(this);
+ this.showDeleteConfirm = this.showDeleteConfirm.bind(this);
+ this.delCert = this.delCert.bind(this);
+ this.closeConfirmDelete = this.closeConfirmDelete.bind(this);
+ this.reloadCerts = this.reloadCerts.bind(this);
+ this.reloadCACerts = this.reloadCACerts.bind(this);
+ }
+
+ handleNavSelect(key) {
+ this.setState({
+ activeKey: key
+ });
+ }
+
+ showAddModal () {
+ this.setState({
+ showAddModal: true,
+ errObj: {certName: true, certFile: true}
+ });
+ }
+
+ closeAddModal () {
+ this.setState({
+ showAddModal: false,
+ certName: "",
+ certFile: "",
+ });
+ }
+
+ showAddCAModal () {
+ this.setState({
+ showAddCAModal: true,
+ errObj: {certName: true, certFile: true}
+ });
+ }
+
+ closeAddCAModal () {
+ this.setState({
+ showAddCAModal: false,
+ certName: "",
+ certFile: "",
+ });
+ }
+
+ addCert () {
+ if (this.state.certName == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate nickname`
+ );
+ return;
+ } else if (this.state.certFile == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate file name`
+ );
+ return;
+ }
+
+ this.setState({
+ modalSpinner: true,
+ loading: true,
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "add", "--name=" + this.state.certName, "--file=" + this.state.certFile
+ ];
+ log_cmd("addCert", "Adding server cert", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ showAddModal: false,
+ certFile: '',
+ certName: '',
+ modalSpinner: false
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully added certificate`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error adding certificate - ${msg}`
+ );
+ });
+ }
+
+ addCACert () {
+ if (this.state.certName == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate nickname`
+ );
+ return;
+ } else if (this.state.certFile == "") {
+ this.props.addNotification(
+ "warning",
+ `Missing certificate file name`
+ );
+ return;
+ }
+
+ this.setState({
+ modalSpinner: true,
+ loading: true,
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ca-certificate", "add", "--name=" + this.state.certName, "--file=" + this.state.certFile
+ ];
+ log_cmd("addCACert", "Adding CA certificate", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ showAddCAModal: false,
+ certFile: '',
+ certName: '',
+ modalSpinner: false,
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully added certificate`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error adding certificate - ${msg}`
+ );
+ });
+ }
+
+ showDeleteConfirm(dataRow) {
+ this.setState({
+ showConfirmDelete: true,
+ certName: dataRow.nickname[0],
+ });
+ }
+
+ delCert () {
+ this.setState({
+ modalSpinner: true,
+ loading: true
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "del", this.state.certName
+ ];
+ log_cmd("delCert", "Deleting certificate", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ certName: '',
+ modalSpinner: false,
+ showConfirmDelete: false,
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully deleted certificate`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ certName: '',
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error deleting certificate - ${msg}`
+ );
+ });
+ }
+
+ showEditModal (rowData) {
+ this.setState({
+ showEditModal: true,
+ certName: rowData.nickname[0],
+ flags: rowData.flags[0],
+ isCACert: false,
+ });
+ }
+
+ closeEditModal () {
+ this.setState({
+ showEditModal: false,
+ flags: ''
+ });
+ }
+
+ showEditCAModal (rowData) {
+ this.setState({
+ showEditModal: true,
+ certName: rowData.nickname[0],
+ flags: rowData.flags[0],
+ isCACert: true,
+ });
+ }
+
+ editCert () {
+ // Check if CA cert flags were removed
+ if (this.state.isCACert) {
+ let SSLFlags = '';
+ SSLFlags = this.state.flags.split(',', 1);
+ if (!SSLFlags[0].includes('C') || !SSLFlags[0].includes('T')) {
+ // This could remove the CA cert properties, better warn user
+ this.setState({
+ showConfirmCAChange: true
+ });
+ return;
+ }
+ }
+ this.doEditCert();
+ }
+
+ closeConfirmCAChange () {
+ this.setState({
+ showConfirmCAChange: false
+ });
+ }
+
+ doEditCert () {
+ this.setState({
+ modalSpinner: true,
+ loading: true,
+ });
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "set-trust-flags", this.state.certName, "--flags=" + this.state.flags
+ ];
+ log_cmd("doEditCert", "Editing trust flags", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.reloadCACerts();
+ this.setState({
+ showEditModal: false,
+ flags: '',
+ certName: '',
+ modalSpinner: false,
+ });
+ this.props.addNotification(
+ "success",
+ `Successfully changed certificate's trust flags`
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.setState({
+ showEditModal: false,
+ flags: '',
+ certName: '',
+ modalSpinner: false,
+ loading: false,
+ });
+ this.props.addNotification(
+ "error",
+ `Error setting trust flags - ${msg}`
+ );
+ });
+ }
+
+ handleAddChange (e) {
+ const value = e.target.value;
+ let valueErr = false;
+ let errObj = this.state.errObj;
+
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ handleFlagChange (e) {
+ const checked = e.target.checked;
+ const id = e.target.id;
+ let flags = this.state.flags;
+ let SSLFlags = '';
+ let EmailFlags = '';
+ let OSFlags = '';
+ [SSLFlags, EmailFlags, OSFlags] = flags.split(',');
+
+ if (id.endsWith('SSL')) {
+ for (let trustFlag of ['C', 'T', 'c', 'P', 'p']) {
+ if (id.startsWith(trustFlag)) {
+ if (checked) {
+ SSLFlags += trustFlag;
+ } else {
+ SSLFlags = SSLFlags.replace(trustFlag, '');
+ }
+ }
+ }
+ } else if (id.endsWith('Email')) {
+ for (let trustFlag of ['C', 'T', 'c', 'P', 'p']) {
+ if (id.startsWith(trustFlag)) {
+ if (checked) {
+ EmailFlags += trustFlag;
+ } else {
+ EmailFlags = EmailFlags.replace(trustFlag, '');
+ }
+ }
+ }
+ } else {
+ // Object Signing (OS)
+ for (let trustFlag of ['C', 'T', 'c', 'P', 'p']) {
+ if (id.startsWith(trustFlag)) {
+ if (checked) {
+ OSFlags += trustFlag;
+ } else {
+ OSFlags = OSFlags.replace(trustFlag, '');
+ }
+ }
+ }
+ }
+ this.setState({
+ flags: SSLFlags + "," + EmailFlags + "," + OSFlags
+ });
+ }
+
+ closeConfirmDelete () {
+ this.setState({
+ showConfirmDelete: false,
+ });
+ }
+
+ reloadCerts () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "list",
+ ];
+ log_cmd("reloadCerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const certs = JSON.parse(content);
+ let certNames = [];
+ for (let cert of certs) {
+ certNames.push(cert.attrs['nickname']);
+ }
+ this.setState({
+ ServerCerts: certs,
+ loading: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error loading server certificates - ${msg}`
+ );
+ });
+ }
+
+ reloadCACerts () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ca-certificate", "list",
+ ];
+ log_cmd("reloadCACerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let certs = JSON.parse(content);
+ this.setState({
+ CACerts: certs,
+ loading: false
+ }, this.reloadCerts);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error loading CA certificates - ${msg}`
+ );
+ });
+ }
+
+ render () {
+ let CATitle = 'Trusted Certificate Authorites <font size="1">(' + this.state.CACerts.length + ')</font>';
+ let ServerTitle = 'TLS Certificates <font size="1">(' + this.state.ServerCerts.length + ')</font>';
+
+ let certificatePage = '';
+
+ if (this.state.loading) {
+ certificatePage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading certificates ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ certificatePage =
+ <div className="container-fluid">
+ <div className="ds-tab-table">
+ <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: CATitle}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: ServerTitle}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-lg">
+ <CertTable
+ certs={this.state.CACerts}
+ key={this.state.CACerts}
+ editCert={this.showEditCAModal}
+ delCert={this.showDeleteConfirm}
+ />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top-med"
+ onClick={() => {
+ this.showAddCAModal();
+ }}
+ >
+ Add CA Certificate
+ </Button>
+ </div>
+ </TabPane>
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <CertTable
+ certs={this.state.ServerCerts}
+ key={this.state.ServerCerts}
+ editCert={this.showEditModal}
+ delCert={this.showDeleteConfirm}
+ />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top-med"
+ onClick={() => {
+ this.showAddModal();
+ }}
+ >
+ Add Server Certificate
+ </Button>
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ </div>;
+ }
+ return (
+ <div>
+ {certificatePage}
+ <EditCertModal
+ showModal={this.state.showEditModal}
+ closeHandler={this.closeEditModal}
+ handleChange={this.handleFlagChange}
+ saveHandler={this.editCert}
+ flags={this.state.flags}
+ spinning={this.state.modalSpinner}
+ />
+ <SecurityAddCertModal
+ showModal={this.state.showAddModal}
+ closeHandler={this.closeAddModal}
+ handleChange={this.handleAddChange}
+ saveHandler={this.addCert}
+ spinning={this.state.modalSpinner}
+ error={this.state.errObj}
+ />
+ <SecurityAddCACertModal
+ showModal={this.state.showAddCAModal}
+ closeHandler={this.closeAddCAModal}
+ handleChange={this.handleAddChange}
+ saveHandler={this.addCACert}
+ spinning={this.state.modalSpinner}
+ error={this.state.errObj}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmDelete}
+ closeHandler={this.closeConfirmDelete}
+ actionFunc={this.delCert}
+ msg="Are you sure you want to delete this certificate?"
+ msgContent={this.state.certName}
+ />
+ <ConfirmPopup
+ showModal={this.state.showConfirmCAChange}
+ closeHandler={this.closeConfirmCAChange}
+ actionFunc={this.doEditCert}
+ msg="Removing the 'C' or 'T' flags from the SSL trust catagory could break all TLS connectivity to and from the server, are you sure you want to proceed?"
+ />
+ </div>
+ );
+ }
+}
+
+// Props and defaults
+
+CertificateManagement.propTypes = {
+ serverId: PropTypes.string,
+ CACerts: PropTypes.array,
+ ServerCerts: PropTypes.array,
+ addNotification: PropTypes.func,
+};
+
+CertificateManagement.defaultProps = {
+ serverId: "",
+ CACerts: [],
+ ServerCerts: [],
+ addNotification: noop,
+};
+
+export default CertificateManagement;
diff --git a/src/cockpit/389-console/src/lib/security/ciphers.jsx b/src/cockpit/389-console/src/lib/security/ciphers.jsx
new file mode 100644
index 0000000..4714fcb
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/ciphers.jsx
@@ -0,0 +1,274 @@
+import React from "react";
+import cockpit from "cockpit";
+import {
+ Button,
+ Row,
+ Col,
+ ControlLabel,
+ Spinner,
+ noop,
+} from "patternfly-react";
+import { log_cmd } from "../../lib/tools.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+import { Typeahead } from "react-bootstrap-typeahead";
+
+export class Ciphers extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ allowCiphers: [],
+ denyCiphers: [],
+ cipherPref: "default",
+ prefs: this.props.cipherPref,
+ saving: false,
+ };
+
+ this.handlePrefChange = this.handlePrefChange.bind(this);
+ this.saveCipherPref = this.saveCipherPref.bind(this);
+ }
+
+ componentWillMount () {
+ let cipherPref = "default";
+ let allowedCiphers = [];
+ let deniedCiphers = [];
+
+ // Parse SSL cipher attributes (nsSSL3Ciphers)
+ if (this.props.cipherPref != "") {
+ let rawCiphers = this.props.cipherPref.split(",");
+
+ // First check the first element as it has special meaning
+ if (rawCiphers[0].toLowerCase() == "default") {
+ rawCiphers.shift();
+ } else if (rawCiphers[0].toLowerCase() == "+all") {
+ cipherPref = "+all";
+ rawCiphers.shift();
+ } else if (rawCiphers[0].toLowerCase() == "-all") {
+ cipherPref = "-all";
+ rawCiphers.shift();
+ }
+
+ // Process the remaining ciphers
+ rawCiphers = rawCiphers.map(function(x) { return x.toUpperCase() });
+ for (let cipher of rawCiphers) {
+ if (cipher.startsWith("+")) {
+ allowedCiphers.push(cipher.substring(1));
+ } else if (cipher.startsWith("-")) {
+ deniedCiphers.push(cipher.substring(1));
+ }
+ }
+ }
+
+ this.setState({
+ cipherPref: cipherPref,
+ allowCiphers: allowedCiphers,
+ denyCiphers: deniedCiphers,
+ });
+ }
+
+ saveCipherPref () {
+ /* start the spinner */
+ this.setState({
+ saving: true
+ });
+ let prefs = this.state.cipherPref;
+ for (let cipher of this.state.allowCiphers) {
+ prefs += ",+" + cipher;
+ }
+ for (let cipher of this.state.denyCiphers) {
+ prefs += ",-" + cipher;
+ }
+
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ciphers", "set", "--", prefs
+ ];
+ log_cmd("saveCipherPref", "Saving cipher preferences", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.props.addNotification(
+ "success",
+ `Successfully set cipher preferences. You must restart the server for these changes to take effect.`
+ );
+ this.setState({
+ saving: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.props.addNotification(
+ "error",
+ `Error setting cipher preferences - ${msg}`
+ );
+ this.setState({
+ saving: false,
+ });
+ });
+ }
+
+ handlePrefChange (e) {
+ this.setState({
+ cipherPref: e.target.value,
+ });
+ }
+
+ render () {
+ let supportedCiphers = [];
+ let enabledCiphers = [];
+ let cipherPage;
+
+ for (let cipher of this.props.supportedCiphers) {
+ if (!this.props.enabledCiphers.includes(cipher)) {
+ // This cipher is not currently enabled, so list it as available
+ supportedCiphers.push(cipher);
+ }
+ }
+ for (let cipher of this.props.enabledCiphers) {
+ enabledCiphers.push(cipher);
+ }
+ let supportedList = supportedCiphers.map((name) =>
+ <option key={name}>{name}</option>
+ );
+ let enabledList = enabledCiphers.map((name) =>
+ <option key={name}>{name}</option>
+ );
+
+ if (this.state.saving) {
+ cipherPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Saving cipher preferences ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ cipherPage =
+ <div className="container-fluid">
+ <div className="ds-container">
+ <div className='ds-inline'>
+ <div>
+ <h4>Enabled Ciphers</h4>
+ </div>
+ <div>
+ <select
+ className="ds-cipher-width"
+ size="16"
+ title="The current ciphers the server is accepting. This is only updated after a server restart"
+ >
+ {enabledList}
+ </select>
+ </div>
+ </div>
+ <div className="ds-divider-lrg" />
+ <div className='ds-inline'>
+ <div>
+ <h4>Other Available Ciphers</h4>
+ </div>
+ <div>
+ <select className="ds-cipher-width" size="16">
+ {supportedList}
+ </select>
+ </div>
+ </div>
+ </div>
+ <hr />
+ <Row>
+ <Col componentClass={ControlLabel} sm={2}>
+ Cipher Suite
+ </Col>
+ <Col sm={9}>
+ <select
+ id="cipherPref"
+ onChange={this.handlePrefChange}
+ defaultValue={this.state.cipherPref}
+ >
+ <option title="default" value="default" key="default">Default Ciphers</option>
+ <option title="+all" value="+all" key="all">All Ciphers</option>
+ <option title="-all" value="-all" key="none">No Ciphers</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={2}>
+ Allow Specific Ciphers
+ </Col>
+ <Col sm={9}>
+ <Typeahead
+ multiple
+ onChange={value => {
+ this.setState({
+ allowCiphers: value
+ });
+ }}
+ selected={this.state.allowCiphers}
+ options={this.props.supportedCiphers}
+ newSelectionPrefix="Add a cipher: "
+ placeholder="Type a cipher..."
+ id="allowCipher"
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top">
+ <Col componentClass={ControlLabel} sm={2}>
+ Deny Specific Ciphers
+ </Col>
+ <Col sm={9}>
+ <Typeahead
+ multiple
+ onChange={value => {
+ this.setState({
+ denyCiphers: value
+ });
+ }}
+ selected={this.state.denyCiphers}
+ options={this.props.supportedCiphers}
+ newSelectionPrefix="Add a cipher: "
+ placeholder="Type a cipher..."
+ id="denyCipher"
+ />
+ </Col>
+ </Row>
+ <p />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top"
+ onClick={() => {
+ this.saveCipherPref();
+ }}
+ >
+ Save Cipher Preferences
+ </Button>
+ </div>;
+ }
+
+ return (
+ <div>
+ {cipherPage}
+ </div>
+ );
+ }
+}
+
+// Props and defaults
+
+Ciphers.propTypes = {
+ serverId: PropTypes.string,
+ supportedCiphers: PropTypes.array,
+ enabledCiphers: PropTypes.array,
+ cipherPref: PropTypes.string,
+ addNotification: PropTypes.func,
+};
+
+Ciphers.defaultProps = {
+ serverId: "",
+ supportedCiphers: [],
+ enabledCiphers: [],
+ cipherPref: "",
+ addNotification: noop,
+};
+
+export default Ciphers;
diff --git a/src/cockpit/389-console/src/lib/security/securityModals.jsx b/src/cockpit/389-console/src/lib/security/securityModals.jsx
new file mode 100644
index 0000000..f8ad49c
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/securityModals.jsx
@@ -0,0 +1,689 @@
+import React from "react";
+import {
+ Modal,
+ Row,
+ Col,
+ ControlLabel,
+ Checkbox,
+ FormControl,
+ Icon,
+ Button,
+ Form,
+ Spinner,
+ noop
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+
+export class SecurityAddCACertModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ error
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Adding CA certificate...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Add Certificate Authority
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>
+ Add CA certificate to the security database.
+ </h4>
+ <hr />
+ <Row title="Enter full path to and and including certificate file name">
+ <Col sm={4}>
+ <ControlLabel>Certificate File</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certFile"
+ className={error.certFile ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row title="Enter name/nickname of the certificate">
+ <Col sm={4}>
+ <ControlLabel>Certificate Nickname</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certName"
+ className={error.certName ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Add Certificate
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class SecurityAddCertModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ spinning,
+ error
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Adding certificate...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Add Certificate
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>
+ Add certificate to the security database.
+ </h4>
+ <hr />
+ <Row title="Enter full path to and and including certificate file name">
+ <Col sm={4}>
+ <ControlLabel>Certificate File</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certFile"
+ className={error.certFile ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row title="Enter name/nickname of the certificate">
+ <Col sm={4}>
+ <ControlLabel>Certificate Nickname</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <FormControl
+ type="text"
+ id="certName"
+ className={error.certName ? "ds-input-bad" : ""}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Add Certificate
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class SecurityEnableModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ primaryName,
+ certs,
+ spinning
+ } = this.props;
+
+ // Build list of cert names for the select list
+ let certNames = [];
+ for (let cert of certs) {
+ certNames.push(cert.attrs['nickname']);
+ }
+ let certNameOptions = certNames.map((name) =>
+ <option key={name} value={name}>{name}</option>
+ );
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Enabling security...
+ </div>
+ </Row>;
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Enable Security
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <h4>
+ You are choosing to enable security for the Directory Server which
+ allows the server to accept incoming client TLS connections. Please
+ select which certificate the server should use.
+ </h4>
+ <hr />
+ <Row className="ds-margin-top" title="The server certificate the Directory Server will use">
+ <Col sm={4}>
+ <ControlLabel>Available Certificates</ControlLabel>
+ </Col>
+ <Col sm={8}>
+ <select id="certNameSelect" onChange={handleChange} defaultValue={primaryName}>
+ {certNameOptions}
+ </select>
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Enable Security
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+export class EditCertModal extends React.Component {
+ render() {
+ const {
+ showModal,
+ closeHandler,
+ handleChange,
+ saveHandler,
+ flags,
+ spinning
+ } = this.props;
+
+ let spinner = "";
+ if (spinning) {
+ spinner =
+ <Row>
+ <div className="ds-modal-spinner">
+ <Spinner loading inline size="lg" />Saving certificate...
+ </div>
+ </Row>;
+ }
+
+ // Process the cert flags
+ let CSSLChecked = false;
+ let CEmailChecked = false;
+ let COSChecked = false;
+ let TSSLChecked = false;
+ let TEmailChecked = false;
+ let TOSChecked = false;
+ let cSSLChecked = false;
+ let cEmailChecked = false;
+ let cOSChecked = false;
+ let PSSLChecked = false;
+ let PEmailChecked = false;
+ let POSChecked = false;
+ let pSSLChecked = false;
+ let pEmailChecked = false;
+ let pOSChecked = false;
+ let uSSLChecked = false;
+ let uEmailChecked = false;
+ let uOSChecked = false;
+ let SSLFlags = '';
+ let EmailFlags = '';
+ let OSFlags = '';
+ if (flags != "") {
+ [SSLFlags, EmailFlags, OSFlags] = flags.split(',');
+ if (SSLFlags.includes('T')) {
+ TSSLChecked = true;
+ }
+ if (EmailFlags.includes('T')) {
+ TEmailChecked = true;
+ }
+ if (OSFlags.includes('T')) {
+ TOSChecked = true;
+ }
+ if (SSLFlags.includes('C')) {
+ CSSLChecked = true;
+ }
+ if (EmailFlags.includes('C')) {
+ CEmailChecked = true;
+ }
+ if (OSFlags.includes('C')) {
+ COSChecked = true;
+ }
+ if (SSLFlags.includes('c')) {
+ cSSLChecked = true;
+ }
+ if (EmailFlags.includes('c')) {
+ cEmailChecked = true;
+ }
+ if (OSFlags.includes('c')) {
+ cOSChecked = true;
+ }
+ if (SSLFlags.includes('P')) {
+ PSSLChecked = true;
+ }
+ if (EmailFlags.includes('P')) {
+ PEmailChecked = true;
+ }
+ if (OSFlags.includes('P')) {
+ POSChecked = true;
+ }
+ if (SSLFlags.includes('p')) {
+ pSSLChecked = true;
+ }
+ if (EmailFlags.includes('p')) {
+ pEmailChecked = true;
+ }
+ if (OSFlags.includes('p')) {
+ pOSChecked = true;
+ }
+ if (SSLFlags.includes('u')) {
+ uSSLChecked = true;
+ }
+ if (EmailFlags.includes('u')) {
+ uEmailChecked = true;
+ }
+ if (OSFlags.includes('u')) {
+ uOSChecked = true;
+ }
+ }
+
+ return (
+ <Modal show={showModal} onHide={closeHandler}>
+ <div className="ds-no-horizontal-scrollbar">
+ <Modal.Header>
+ <button
+ className="close"
+ onClick={closeHandler}
+ aria-hidden="true"
+ aria-label="Close"
+ >
+ <Icon type="pf" name="close" />
+ </button>
+ <Modal.Title>
+ Edit Certificate Trust Flags
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form horizontal autoComplete="off">
+ <Row className="ds-margin-top">
+ <Col sm={4}>
+ <ControlLabel>Flags</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <ControlLabel>SSL</ControlLabel>
+ </Col>
+ <Col sm={2}>
+ <ControlLabel>Email</ControlLabel>
+ </Col>
+ <Col sm={3}>
+ <ControlLabel>Object Signing</ControlLabel>
+ </Col>
+ </Row>
+ <hr />
+ <Row>
+ <Col sm={4} title="Trusted CA (flag 'C', also implies 'c' flag)">
+ (C) - Trusted CA
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="CflagSSL"
+ checked={CSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="CflagEmail"
+ checked={CEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="CflagOS"
+ checked={COSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Trusted CA for client authentication (flag 'T')">
+ (T) - Trusted CA Client Auth
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="TflagSSL"
+ checked={TSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="TflagEmail"
+ checked={TEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="TflagOS"
+ checked={TOSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Valid CA (flag 'c')">
+ (c) - Valid CA
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="cflagSSL"
+ checked={cSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="cflagEmail"
+ checked={cEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="cflagOS"
+ checked={cOSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Trusted Peer (flag 'P', implies flag 'p')">
+ (P) - Trusted Peer
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="PflagSSL"
+ checked={PSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="PflagEmail"
+ checked={PEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="PflagOS"
+ checked={POSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={4} title="Valid Peer (flag 'p')">
+ (p) - Valid Peer
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="pflagSSL"
+ checked={pSSLChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="pflagEmail"
+ checked={pEmailChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="pflagOS"
+ checked={pOSChecked}
+ onChange={handleChange}
+ />
+ </Col>
+ </Row>
+ <hr />
+ <Row>
+ <Col sm={4} title="A private key is associated with the certificate. This is a dynamic flag and you cannot adjust it.">
+ (u) - Private Key
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="uflagSSL"
+ checked={uSSLChecked}
+ disabled
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="uflagEmail"
+ checked={uEmailChecked}
+ disabled
+ />
+ </Col>
+ <Col sm={2}>
+ <Checkbox
+ id="uflagOS"
+ checked={uOSChecked}
+ disabled
+ />
+ </Col>
+ </Row>
+ <p />
+ {spinner}
+ </Form>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button
+ bsStyle="default"
+ className="btn-cancel"
+ onClick={closeHandler}
+ >
+ Cancel
+ </Button>
+ <Button
+ bsStyle="primary"
+ onClick={saveHandler}
+ >
+ Save
+ </Button>
+ </Modal.Footer>
+ </div>
+ </Modal>
+ );
+ }
+}
+
+SecurityEnableModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ primaryName: PropTypes.string,
+ certs: PropTypes.array,
+ spinning: PropTypes.bool,
+};
+
+SecurityEnableModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ primaryName: "",
+ certs: [],
+ spinning: false,
+};
+
+EditCertModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ flags: PropTypes.string,
+ spinning: PropTypes.bool,
+};
+
+EditCertModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ flags: "",
+ spinning: false,
+};
+
+SecurityAddCertModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+SecurityAddCertModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ error: {},
+};
+
+SecurityAddCACertModal.propTypes = {
+ showModal: PropTypes.bool,
+ closeHandler: PropTypes.func,
+ handleChange: PropTypes.func,
+ saveHandler: PropTypes.func,
+ spinning: PropTypes.bool,
+ error: PropTypes.object,
+};
+
+SecurityAddCACertModal.defaultProps = {
+ showModal: false,
+ closeHandler: noop,
+ handleChange: noop,
+ saveHandler: noop,
+ spinning: false,
+ error: {},
+};
diff --git a/src/cockpit/389-console/src/lib/security/securityTables.jsx b/src/cockpit/389-console/src/lib/security/securityTables.jsx
new file mode 100644
index 0000000..6b74a01
--- /dev/null
+++ b/src/cockpit/389-console/src/lib/security/securityTables.jsx
@@ -0,0 +1,454 @@
+import React from "react";
+import {
+ // Button,
+ DropdownButton,
+ MenuItem,
+ actionHeaderCellFormatter,
+ sortableHeaderCellFormatter,
+ tableCellFormatter,
+ noop
+} from "patternfly-react";
+import { DSTable } from "../dsTable.jsx";
+import PropTypes from "prop-types";
+import "../../css/ds.css";
+
+class CertTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "nickname",
+ columns: [
+ {
+ property: "nickname",
+ header: {
+ label: "Certificate Name",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "subject",
+ header: {
+ label: "Subject DN",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "issuer",
+ header: {
+ label: "Issued By",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "flags",
+ header: {
+ label: "Trust Flags",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "expires",
+ header: {
+ label: "Expiration Date",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "action",
+ header: {
+ label: "",
+ props: {
+ index: 5,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 5
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.nickname[0]}>
+ <DropdownButton id={rowData.nickname[0]}
+ bsStyle="default" title="Actions">
+ <MenuItem eventKey="1" onClick={() => {
+ this.props.editCert(rowData);
+ }}
+ >
+ Edit Trust Flags
+ </MenuItem>
+ <MenuItem divider />
+ <MenuItem eventKey="2" onClick={() => {
+ this.props.delCert(rowData);
+ }}
+ >
+ Delete Certificate
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ this.getSingleColumn = this.getSingleColumn.bind(this);
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Certificates",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ let certRows = [];
+ let serverTable;
+ for (let cert of this.props.certs) {
+ let obj = {
+ 'nickname': [cert.attrs['nickname']],
+ 'subject': [cert.attrs['subject']],
+ 'issuer': [cert.attrs['issuer']],
+ 'expires': [cert.attrs['expires']],
+ 'flags': [cert.attrs['flags']],
+ };
+ certRows.push(obj);
+ }
+
+ if (certRows.length == 0) {
+ serverTable = <DSTable
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "No Certificates"}]}
+ key={"nocerts"}
+ />;
+ } else {
+ serverTable = <DSTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={certRows}
+ key={certRows}
+ disableLoadingSpinner
+ />;
+ }
+
+ return (
+ <div>
+ {serverTable}
+ </div>
+ );
+ }
+}
+
+// Future - https://pagure.io/389-ds-base/issue/50491
+class CRLTable extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ rowKey: "name",
+ columns: [
+ {
+ property: "name",
+ header: {
+ label: "Issued By",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "effective",
+ header: {
+ label: "Effective Date",
+ props: {
+ index: 1,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 1
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "nextUpdate",
+ header: {
+ label: "Next Updateo",
+ props: {
+ index: 2,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 2
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+
+ {
+ property: "type",
+ header: {
+ label: "Type",
+ props: {
+ index: 3,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 3
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ {
+ property: "action",
+ header: {
+ label: "",
+ props: {
+ index: 4,
+ rowSpan: 1,
+ colSpan: 1
+ },
+ formatters: [actionHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 4
+ },
+ formatters: [
+ (value, { rowData }) => {
+ return [
+ <td key={rowData.name[0]}>
+ <DropdownButton id={rowData.name[0]}
+ bsStyle="default" title="Actions">
+ <MenuItem eventKey="1" onClick={() => {
+ this.props.editIndex(rowData);
+ }}
+ >
+ View CRL
+ </MenuItem>
+ <MenuItem eventKey="2" onClick={() => {
+ this.props.reindexIndex(rowData);
+ }}
+ >
+ Delete CRL
+ </MenuItem>
+ </DropdownButton>
+ </td>
+ ];
+ }
+ ]
+ }
+ }
+ ]
+ };
+ this.getColumns = this.getColumns.bind(this);
+ }
+
+ getSingleColumn () {
+ return [
+ {
+ property: "msg",
+ header: {
+ label: "Certificate Revocation Lists",
+ props: {
+ index: 0,
+ rowSpan: 1,
+ colSpan: 1,
+ sort: true
+ },
+ transforms: [],
+ formatters: [],
+ customFormatters: [sortableHeaderCellFormatter]
+ },
+ cell: {
+ props: {
+ index: 0
+ },
+ formatters: [tableCellFormatter]
+ }
+ },
+ ];
+ }
+
+ getColumns() {
+ return this.state.columns;
+ }
+
+ render() {
+ let crlTable;
+ if (this.props.rows.length == 0) {
+ crlTable = <DSTable
+ getColumns={this.getSingleColumn}
+ rowKey={"msg"}
+ rows={[{msg: "None"}]}
+ />;
+ } else {
+ crlTable = <DSTable
+ getColumns={this.getColumns}
+ rowKey={this.state.rowKey}
+ rows={this.props.rows}
+ disableLoadingSpinner
+ />;
+ }
+ return (
+ <div>
+ {crlTable}
+ </div>
+ );
+ }
+}
+
+// Props and defaults
+
+CertTable.propTypes = {
+ // serverId: PropTypes.string,
+ certs: PropTypes.array,
+ editCert: PropTypes.func,
+ delCert: PropTypes.func,
+};
+
+CertTable.defaultProps = {
+ // serverId: "",
+ certs: [],
+ editCert: noop,
+ delCert: noop,
+};
+
+export {
+ CertTable,
+ CRLTable
+};
diff --git a/src/cockpit/389-console/src/lib/tools.jsx b/src/cockpit/389-console/src/lib/tools.jsx
index 5d482ba..b3e7573 100644
--- a/src/cockpit/389-console/src/lib/tools.jsx
+++ b/src/cockpit/389-console/src/lib/tools.jsx
@@ -1,8 +1,8 @@
export function searchFilter(searchFilterValue, columnsToSearch, rows) {
if (searchFilterValue && rows && rows.length) {
- const filteredRows = [];
+ let filteredRows = [];
rows.forEach(row => {
- var rowToSearch = [];
+ let rowToSearch = [];
if (columnsToSearch && columnsToSearch.length) {
columnsToSearch.forEach(column =>
rowToSearch.push(row[column])
@@ -27,18 +27,18 @@ export function searchFilter(searchFilterValue, columnsToSearch, rows) {
export function log_cmd(js_func, desc, cmd_array) {
if (console) {
- var pw_args = ["--passwd", "--bind-pw"];
- var cmd_list = [];
- var converted_pw = false;
+ let pw_args = ["--passwd", "--bind-pw"];
+ let cmd_list = [];
+ let converted_pw = false;
- for (var idx in cmd_array) {
- var cmd = cmd_array[idx];
+ for (let idx in cmd_array) {
+ let cmd = cmd_array[idx];
converted_pw = false;
for (var arg_idx in pw_args) {
if (cmd.startsWith(pw_args[arg_idx])) {
// We are setting a password, if it has a value we need to hide it
- var arg_len = cmd.indexOf("=");
- var arg = cmd.substring(0, arg_len);
+ let arg_len = cmd.indexOf("=");
+ let arg = cmd.substring(0, arg_len);
if (cmd.length != arg_len + 1) {
// We are setting a password value...
cmd_list.push(arg + "=**********");
diff --git a/src/cockpit/389-console/src/security.html b/src/cockpit/389-console/src/security.html
deleted file mode 100644
index 1444418..0000000
--- a/src/cockpit/389-console/src/security.html
+++ /dev/null
@@ -1,502 +0,0 @@
-
- <div id="sec-config" class="security-ctrl all-pages" hidden>
- <h3 class="ds-config-header">Security Configuration</h3>
-
- <input type="checkbox" class="ds-config-checkbox" id="nsslapd-security"><label
- for="nsslapd-security" class="ds-label" title="Enable security in the server (nsslapd-security)."> Enable Security</label>
- <hr class="">
- <div class="ds-expired-div" id="cert-attrs">
-
- <div class="ds-container">
- <div class="ds-inline">
- <div>
- <label for="nsslapd-secureport" class="ds-config-label" title="The server's secure port number (nsslapd-secureport).">Server Secure Port</label><input
- class="ds-input" type="text" id="nsslapd-secureport" size="20"/>
- </div>
- <div>
- <label for="nsslapd-securelistenhost" class="ds-config-label" title="This parameter can be used to restrict the Directory Server instance to a single IP interface (hostname, or IP address). This parameter specifically sets what interface to use for TLS traffic. Requires restart. (nsslapd-securelistenhost).">
- Secure Listen Host Address</label><input class="ds-input" type="text" id="nsslapd-securelistenhost" size="20"/>
- </div>
- <div>
- <label for="sec-sslmin" class="ds-config-label" title="The minimum SSL/TLS version the server will accept (sslversionmin).">Minimum SSL/TLS Version </label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-sslmin">
- <option>TLS1.3</option>
- <option>TLS1.2</option>
- <option>TLS1.1</option>
- <option>TLS1.0</option>
- <option>SSL3</option>
- </select>
- </div>
- <div>
- <label for="sec-sslmax" class="ds-config-label" title="The maximum SSL/TLS version the server will accept (sslversionmax)."> Maximum SSL/TLS Version</label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-sslmax">
- <option>TLS1.3</option>
- <option>TLS1.2</option>
- <option>TLS1.1</option>
- <option>TLS1.0</option>
- <option>SSL3</option>
- </select>
- </div>
- <div>
- <label for="sec-clientauth" class="ds-config-label" title="shows how the Directory Server enforces client authentication (nsSSLClientAuth)."> Client Authentication</label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-clientauth">
- <option>Off</option>
- <option>Allowed</option>
- <option>Required</option>
- </select>
- </div>
- <div>
- <label for="sec-validate" class="ds-config-label" title="Validate server's certificate expiration date (nsslapd-validate-cert)."> Validate Certificate Expiration</label><select
- class="btn btn-default dropdown ds-sec-dropdown" id="sec-validate">
- <option>Warn</option>
- <option>On</option>
- <option>Off</option>
- </select>
- </div>
- </div>
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
- <div class="ds-divider"></div>
- <div class="ds-line">
- <div>
- <input type="checkbox" class="ds-config-checkbox" id="nsslapd-require-secure-binds"><label
- for="nsslapd-require-secure-binds" class="ds-label" title="Require all connections use TLS (nsslapd-require-secure-binds)."> Require Secure Connections</label>
- </div>
- <div>
- <input type="checkbox" class="ds-config-checkbox" id="nsslapd-ssl-check-hostname"><label
- for="nsslapd-ssl-check-hostname" class="ds-label" title="Verify authenticity of a request by matching the host name against the value assigned to the common name (cn) attribute of the subject name (subjectDN field) in the certificate being presented. (nsslapd-ssl-check-hostname)."> Verify Certificate Subject Hostname</label>
- </div>
- <div>
- <input type="checkbox" class="ds-config-checkbox" id="allowWeakCipher"><label
- for="allowWeakCipher" class="ds-label" title="Allow weak ciphers (allowWeakCipher)."> Allow Weak Ciphers</label>
- </div>
- <div class="ds-first">
- <button class="btn btn-default" id="set-sec-passwd-btn" title="Change the Security Database password">Set Security Password</button>
- </div>
- </div>
- </div>
- <hr>
-
- <h4>Allowed Ciphers</h4>
- <div class="ds-indent">
- <input type="checkbox" class="ds-config-checkbox" id="cipher-default-state"><label
- for="cipher-default-state" class="ds-label" title="Use the preferred default ciphers, as opposed to allowing all the ciphers">Use Default Ciphers</label>
- </div>
- <div id="cipher-table">
- <table class="table table-striped table-bordered table-hover ds-loglevel-table" id="allowed-cipher-table">
- <thead>
- <tr>
- <th class="ds-table-btn">State</th>
- <th>Cipher</th>
- </tr>
- </thead>
- <tbody>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown" id="cipher-all-state">
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td id="cipher-all">All</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown">
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown">
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_128_GCM_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_256_GCM_SHA384</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_AES_128_GCM_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_AES_256_GCM_SHA384</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_AES_128_GCM_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_SEED_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_RSA_WITH_CAMELLIA_256_CBC_SHA</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_AES_256_CBC_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_AES_256_CBC_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_RSA_WITH_AES_128_CBC_SHA256</td>
- </tr>
- <tr class="cipher-row">
- <td class="ds-table-btn"> <select class="btn btn-default dropdown ds-cipher-dropdown" >
- <option></option>
- <option>Enabled</option>
- <option>Disabled</option>
- </select>
- </td>
- <td>TLS_DHE_DSS_WITH_RC4_128_SHA</td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div class="ds-footer">
- <button class="btn btn-primary save-button">Save</button>
- </div>
- </div>
-
-
- <div class="security-ctrl all-pages" id="sec-ciphers-page" hidden>
- <h3 class="ds-config-header">Supported Ciphers</h3>
- <table id="nssslsupportedciphers" class="display ds-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Cipher Name</th>
- <th>Symmetric Cipher Name</th>
- <th>Mac Algorithm Name</th>
- <th>Symmetric Key Bits</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>TLS_RSA_WITH_AES_256_CBC_SHA256</td>
- <td>AES</td>
- <td>SHA256</td>
- <td>256</td>
- </tr>
- <tr>
- <td>TLS_DHE_DSS_WITH_DES_CBC_SHA</td>
- <td>AES</td>
- <td>SHA256</td>
- <td>256</td>
- </tr>
- </tbody>
- </table>
- </div>
-
- <div id="sec-cacert-page" class="all-pages" hidden>
- <h3 class="ds-config-header">CA Certificates</h3>
- <table id="ca-cert-table" class="display ds-repl-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>CA Certificate Name</th>
- <th>Trust Attributes</th>
- <th>Expiration Date</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>CA Certificate</td>
- <td>CTu,u,u</td>
- <td>2020/12/31</td>
- <td>
- <div class="dropdown">
- <button class="btn btn-default dropdown-toggle ds-agmt-dropdown-button" type="button" id="menu1" data-toggle="dropdown">Choose Action...
- <span class="caret"></span></button>
- <ul id="cert-dropdown" class="dropdown-menu ds-agmt-dropdown" role="menu" aria-labelledby="menu1">
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">View Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Edit Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Verify Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Export Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="ca-cert-dropdown" href="#">Delete Certificate</a></li>
- </ul>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- <button class="btn btn-primary" id="import-ca-cert" data-toggle="modal" data-target="#import-cacert-form">Import CA Certificate</button>
- </div>
-
- <div id="sec-svrcert-page" class="all-pages" hidden>
- <h3 class="ds-config-header">Server Certificates</h3>
- <table id="server-cert-table" class="display ds-repl-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Server Certificate Name</th>
- <th>Trust Attributes</th>
- <th>Issued To</th>
- <th>Issued By</th>
- <th>Expiration Date</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>Server-Cert</td>
- <td>u,u,Pu</td>
- <td>localhost.localdomain</td>
- <td>Mark's CA Cert</td>
- <td>2020/11/22</td>
- <td>
- <div class="dropdown">
- <button class="btn btn-default dropdown-toggle" type="button" id="menu1" data-toggle="dropdown">Choose Action...
- <span class="caret"></span></button>
- <ul id="cert-dropdown" class="dropdown-menu" role="menu" aria-labelledby="menu1">
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">View Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Verify Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Renew Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Export Certificate</a></li>
- <li role="certificate"><a role="menuitem" class="server-cert-dropdown" href="#">Delete Certificate</a></li>
- </ul>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- <button class="btn btn-default ds-spacing-sm" id="import-server-cert" data-toggle="modal" data-target="#import-cert-form">Import Certificate</button>
- <button class="btn btn-default" id="import-server-cert">Request Certificate</button>
- </div>
-
- <div id="sec-revoked-page" class="all-pages" hidden>
- <h3 class="ds-config-header">Revoked Certificates</h3>
- <table id="revoked-cert-table" class="display ds-repl-table" cellspacing="0" width="100%">
- <thead>
- <tr class="ds-table-header">
- <th>Issued By</th>
- <th>Effective Date</th>
- <th>Next Update</th>
- <th>Type</th>
- <th>Actions</th>
- </tr>
- </thead>
- <tbody id="cipher-body">
- <tr>
- <td>Mark's CA Cert2</td>
- <td>2018/11/22</td>
- <td>2019/11/22</td>
- <td>CRL</td>
- <td>
- <div class="dropdown">
- <button class="btn btn-default dropdown-toggle ds-agmt-dropdown-button" type="button" id="menu1" data-toggle="dropdown">Choose Action...
- <span class="caret"></span></button>
- <ul id="cert-dropdown" class="dropdown-menu ds-agmt-dropdown" role="menu" aria-labelledby="menu1">
- <li role="certificate"><a role="menuitem" class="revoked-cert-dropdown" href="#">View</a></li>
- <li role="certificate"><a role="menuitem" class="revoked-cert-dropdown" href="#">Delete</a></li>
- </ul>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- <button class="btn btn-default ds-spacing-sm" id="add-revoked-btn" data-toggle="modal" data-target="#revoked-form">Add CRL/CKL</button>
- </div>
-
- <!-- Modals/Popups/Wizards -->
-
-
-
- <div class="modal fade" id="import-cert-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="import-cert-label" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title" id="import-cert-label">Import Server Certificate</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-inline">
- <label for="import-cert-file" class="" title="The name of the database link.">Certificate File</label><input
- class="ds-input ds-left-margin" type="text" id="import-cert-file" name="name" size="40">
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" class="btn btn-primary" id="import-cert-btn" data-dismiss="modal">Import Certificate</button>
- </div>
- </div>
- </div>
- </div>
-
- <div class="modal fade" data-backdrop="static" id="import-cacert-form" tabindex="-1" role="dialog" aria-labelledby="import-cacert-label" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title" id="import-cacert-label">Import CA Certificate</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-inline">
- <label for="import-cert-file" class="" title="The name of the database link.">Certificate File</label><input
- class="ds-input ds-left-margin" type="text" id="import-cacert-file" name="name" size="40">
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" class="btn btn-primary" id="import-cacert-btn" data-dismiss="modal">Import Certificate</button>
- </div>
- </div>
- </div>
- </div>
-
-
- <div class="modal fade" id="revoked-form" data-backdrop="static" tabindex="-1" role="dialog" aria-labelledby="revoked-label" aria-hidden="true">
- <div class="modal-dialog ds-modal-wide">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
- <span class="pficon pficon-close"></span>
- </button>
- <h4 class="modal-title" id="revoked-label">Add Certificate Revocation List/Compromised Key List</h4>
- </div>
- <div class="modal-body">
- <form class="form-horizontal">
- <div class="ds-inline">
- <label for="revoked-file">CRL/CKL PEM File</label><input
- class="ds-input ds-left-margin" type="text" id="revoked-file" name="name" size="40">
- </div>
- </form>
- </div>
- <div class="modal-footer ds-modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
- <button type="button" class="btn btn-primary" id="add-crl-btn" data-dismiss="modal">Add</button>
- </div>
- </div>
- </div>
- </div>
-
-
-
-
- <div id="export-cert" class="modal">
- <form class="modal-content animate">
- <div class="container">
- <h3 id=""><b>Export Certificate</b> <span class="close" id="export-cert-close">×</span></h3>
-
- <div class="clearfix ds-container">
- <div class="ds-panel-left">
- <button type="button" id="export-cert-cancel" class="ds-button-left">Cancel</button>
- </div>
- <div class="ds-panel-right">
- <button type="submit" id="export-cert-save" class="ds-button-right">Export Certificate</button>
- </div>
- </div>
- </div>
- </form>
- </div>
-
-
diff --git a/src/cockpit/389-console/src/security.js b/src/cockpit/389-console/src/security.js
deleted file mode 100644
index fd76dbb..0000000
--- a/src/cockpit/389-console/src/security.js
+++ /dev/null
@@ -1,137 +0,0 @@
-
-
-// TODO clear form functions
-
-
-$(document).ready( function() {
- $("#security-content").load("security.html", function () {
- // default setting
- $('#cert-attrs *').attr('disabled', true);
-
- $(".dropdown").on("change", function() {
- // Refreshes dropdown on Chrome
- $(this).blur();
- });
-
- $("#sec-config-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-config").show();
- });
-
- $("#sec-cacert-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-cacert-page").show();
- });
-
- $("#sec-srvcert-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-svrcert-page").show();
- });
- $("#sec-revoked-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-revoked-page").show();
- });
- $("#sec-ciphers-btn").on("click", function() {
- $(".all-pages").hide();
- $("#security-content").show();
- $("#sec-ciphers-page").show();
- });
-
- $("#sec-config").show();
-
- // Clear forms as theyare clicked
-
- $("#add-revoked-btn").on('click', function () {
- // TODO Clear form
-
- });
-
- $("#add-crl-btn").on('click', function () {
- // Add CRL/CKL
-
- // Close form
- $("#revoked-form").modal("toggle");
- });
-
- $('#nssslsupportedciphers').DataTable( {
- "paging": true,
- "bAutoWidth": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No ciphers!"
- }
- });
-
- $("#nsslapd-security").change(function() {
- if(this.checked) {
- $('#cert-attrs *').attr('disabled', false);
- } else {
- $('#cert-attrs *').attr('disabled', true);
- }
- });
-
- $("#cipher-default-state").change(function() {
- if(this.checked) {
- $("#cipher-table").hide();
- } else {
- $("#cipher-table").show();
- }
- });
-
- // Set up ca cert table
- $('#ca-cert-table').DataTable( {
- "paging": false,
- "bAutoWidth": false,
- "searching": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No Certificates In Database"
- },
- "columnDefs": [ {
- "targets": 3,
- "orderable": false
- } ]
- });
-
- // Set up server cert table
- $('#server-cert-table').DataTable( {
- "paging": false,
- "bAutoWidth": false,
- "searching": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No Certificates In Database"
- },
- "columnDefs": [ {
- "targets": 5,
- "orderable": false
- } ]
- });
-
- // Set up revoked cert table
- $('#revoked-cert-table').DataTable( {
- "paging": false,
- "bAutoWidth": false,
- "searching": false,
- "dom": '<"pull-left"f><"pull-right"l>tip',
- "lengthMenu": [ 10, 25, 50, 100],
- "language": {
- "emptyTable": "No Certificates In Database"
- },
- "columnDefs": [ {
- "targets": 4,
- "orderable": false
- } ]
- });
- // Page is loaded, mark it as so...
- security_page_loaded = 1;
- });
-});
-
diff --git a/src/cockpit/389-console/src/security.jsx b/src/cockpit/389-console/src/security.jsx
new file mode 100644
index 0000000..43edf49
--- /dev/null
+++ b/src/cockpit/389-console/src/security.jsx
@@ -0,0 +1,853 @@
+import cockpit from "cockpit";
+import React from "react";
+import Switch from "react-switch";
+import { NotificationController, ConfirmPopup } from "./lib/notifications.jsx";
+import { log_cmd } from "./lib/tools.jsx";
+import { Typeahead } from "react-bootstrap-typeahead";
+import { CertificateManagement } from "./lib/security/certificateManagement.jsx";
+import { SecurityEnableModal } from "./lib/security/securityModals.jsx";
+import { Ciphers } from "./lib/security/ciphers.jsx";
+import {
+ Nav,
+ NavItem,
+ TabContainer,
+ TabContent,
+ TabPane,
+ Col,
+ Row,
+ ControlLabel,
+ Button,
+ Checkbox,
+ Spinner
+} from "patternfly-react";
+import PropTypes from "prop-types";
+import "./css/ds.css";
+
+export class Security extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ loaded: false,
+ saving: false,
+ notifications: [],
+ activeKey: 1,
+
+ errObj: {},
+ showConfirmDisable: false,
+ showSecurityEnableModal: false,
+ primaryCertName: '',
+ serverCertNames: [],
+ serverCerts: [],
+ // Ciphers
+ supportedCiphers: [],
+ enabledCiphers: [],
+ // Config settings
+ securityEnabled: false,
+ requireSecureBinds: false,
+ secureListenhost: false,
+ securePort: '636',
+ clientAuth: false,
+ checkHostname: false,
+ validateCert: '',
+ sslVersionMin: '',
+ sslVersionMax: '',
+ allowWeakCipher: false,
+ nssslpersonalityssl: '',
+ // Original config Settings
+ _securityEnabled: false,
+ _requireSecureBinds: false,
+ _secureListenhost: false,
+ _securePort: '636',
+ _clientAuth: false,
+ _checkHostname: false,
+ _validateCert: '',
+ _sslVersionMin: '',
+ _sslVersionMax: '',
+ _allowWeakCipher: false,
+ _nssslpersonalityssl: '',
+ };
+
+ this.handleChange = this.handleChange.bind(this);
+ this.addNotification = this.addNotification.bind(this);
+ this.removeNotification = this.removeNotification.bind(this);
+ this.handleNavSelect = this.handleNavSelect.bind(this);
+ this.handleSwitchChange = this.handleSwitchChange.bind(this);
+ this.handleTypeaheadChange = this.handleTypeaheadChange.bind(this);
+ this.loadSecurityConfig = this.loadSecurityConfig.bind(this);
+ this.loadEnabledCiphers = this.loadEnabledCiphers.bind(this);
+ this.loadSupportedCiphers = this.loadSupportedCiphers.bind(this);
+ this.loadCerts = this.loadCerts.bind(this);
+ this.loadCACerts = this.loadCACerts.bind(this);
+ this.closeConfirmDisable = this.closeConfirmDisable.bind(this);
+ this.enableSecurity = this.enableSecurity.bind(this);
+ this.disableSecurity = this.disableSecurity.bind(this);
+ this.saveSecurityConfig = this.saveSecurityConfig.bind(this);
+ this.closeSecurityEnableModal = this.closeSecurityEnableModal.bind(this);
+ }
+
+ addNotification(type, message, timerdelay, persistent) {
+ this.setState(prevState => ({
+ notifications: [
+ ...prevState.notifications,
+ {
+ key: prevState.notifications.length + 1,
+ type: type,
+ persistent: persistent,
+ timerdelay: timerdelay,
+ message: message,
+ }
+ ]
+ }));
+ }
+
+ removeNotification(notificationToRemove) {
+ this.setState({
+ notifications: this.state.notifications.filter(
+ notification => notificationToRemove.key !== notification.key
+ )
+ });
+ }
+
+ componentWillMount () {
+ if (!this.state.loaded) {
+ this.setState({securityEnabled: true}, this.setState({securityEnabled: false}));
+ this.loadSecurityConfig();
+ }
+ }
+
+ loadSupportedCiphers () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ciphers", "list", "--supported"
+ ];
+ log_cmd("loadSupportedCiphers", "Load the security configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ this.setState({
+ supportedCiphers: config.items
+ }, this.loadEnabledCiphers);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security configuration - ${msg}`
+ );
+ });
+ }
+
+ loadEnabledCiphers () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ciphers", "list", "--enabled"
+ ];
+ log_cmd("loadEnabledCiphers", "Load the security configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ this.setState({
+ enabledCiphers: config.items,
+ }, this.loadCerts);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security configuration - ${msg}`
+ );
+ });
+ }
+
+ loadCACerts () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "ca-certificate", "list",
+ ];
+ log_cmd("loadCACerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ let certs = JSON.parse(content);
+ this.setState(() => (
+ {
+ CACerts: certs,
+ loaded: true
+ })
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading CA certificates - ${msg}`
+ );
+ });
+ }
+
+ loadCerts () {
+ // Set loaded: true
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "certificate", "list",
+ ];
+ log_cmd("loadCerts", "Load certificates", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const certs = JSON.parse(content);
+ let certNames = [];
+ for (let cert of certs) {
+ certNames.push(cert.attrs['nickname']);
+ }
+ this.setState(() => (
+ {
+ serverCerts: certs,
+ serverCertNames: certNames,
+ }), this.loadCACerts
+ );
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading server certificates - ${msg}`
+ );
+ });
+ }
+
+ loadRSAConfig() {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "rsa", "get"
+ ];
+ log_cmd("loadRSAConfig", "Load the RSA configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ const nickname = config.items['nssslpersonalityssl'];
+ this.setState(() => (
+ {
+ nssslpersonalityssl: nickname,
+ _nssslpersonalityssl: nickname,
+ }
+ ), this.loadSupportedCiphers);
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security RSA configuration - ${msg}`
+ );
+ });
+ }
+
+ loadSecurityConfig(saving) {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "get"
+ ];
+ log_cmd("loadSecurityConfig", "Load the security configuration", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(content => {
+ const config = JSON.parse(content);
+ const attrs = config.items;
+ let secEnabled = false;
+ let secReqSecBinds = false;
+ let clientAuth = "allowed";
+ let validateCert = "warn";
+ let cipherPref = "default";
+ let allowWeak = false;
+
+ if ('nsslapd-security' in attrs) {
+ if (attrs['nsslapd-security'].toLowerCase() == "on") {
+ secEnabled = true;
+ }
+ }
+ if ('nsslapd-require-secure-binds' in attrs) {
+ if (attrs['nsslapd-require-secure-binds'].toLowerCase() == "on") {
+ secReqSecBinds = true;
+ }
+ }
+ if ('nssslclientauth' in attrs) {
+ if (attrs['nssslclientauth'] != "") {
+ clientAuth = attrs['nssslclientauth'];
+ }
+ }
+ if ('nsslapd-validate-cert' in attrs) {
+ if (attrs['nsslapd-validate-cert'] != "") {
+ validateCert = attrs['nsslapd-validate-cert'].toLowerCase();
+ }
+ }
+ if ('allowweakcipher' in attrs) {
+ if (attrs['allowweakcipher'].toLowerCase() == "on") {
+ allowWeak = true;
+ }
+ }
+ if ('nsssl3ciphers' in attrs) {
+ if (attrs['nsssl3ciphers'] != "") {
+ cipherPref = attrs['nsssl3ciphers'];
+ }
+ }
+
+ this.setState(() => (
+ {
+ securityEnabled: secEnabled,
+ requireSecureBinds: secReqSecBinds,
+ secureListenhost: attrs['nsslapd-securelistenhost'],
+ securePort: attrs['nsslapd-secureport'],
+ clientAuth: clientAuth,
+ checkHostname: attrs['nsslapd-ssl-check-hostname'],
+ validateCert: validateCert,
+ sslVersionMin: attrs['sslversionmin'],
+ sslVersionMax: attrs['sslversionmax'],
+ allowWeakCipher: allowWeak,
+ cipherPref: cipherPref,
+ _securityEnabled: secEnabled,
+ _requireSecureBinds: secReqSecBinds,
+ _secureListenhost: attrs['nsslapd-securelistenhost'],
+ _securePort: attrs['nsslapd-secureport'],
+ _clientAuth: clientAuth,
+ _checkHostname: attrs['nsslapd-ssl-check-hostname'],
+ _validateCert: validateCert,
+ _sslVersionMin: attrs['sslversionmin'],
+ _sslVersionMax: attrs['sslversionmax'],
+ _allowWeakCipher: allowWeak,
+ }
+ ), function() {
+ if (!saving) {
+ this.loadRSAConfig();
+ }
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error loading security configuration - ${msg}`
+ );
+ });
+ }
+
+ handleNavSelect(key) {
+ this.setState({
+ activeKey: key
+ });
+ }
+
+ handleSwitchChange(value) {
+ if (!value) {
+ // We are disabling security, ask for confirmation
+ this.setState({showConfirmDisable: true});
+ } else {
+ // Check if we have certs, if we do make the user choose one from dropdown list, otherwise reject the
+ // enablement
+ if (this.state.serverCerts.length > 0) {
+ this.setState({
+ primaryCertName: this.state.nssslpersonalityssl,
+ showSecurityEnableModal: true,
+ });
+ } else {
+ this.addNotification(
+ "error",
+ `There must be at least one server certificate present in the security database to enable security`
+ );
+ }
+ }
+ }
+
+ closeSecurityEnableModal () {
+ this.setState({
+ showSecurityEnableModal: false,
+ });
+ }
+
+ handleSecEnableChange (e) {
+ const value = e.target.value.trim();
+ this.setState({
+ primaryCertName: value,
+ });
+ }
+
+ closeConfirmDisable () {
+ this.setState({
+ showConfirmDisable: false
+ });
+ }
+
+ enableSecurity () {
+ /* start the spinner */
+ this.setState({
+ secEnableSpinner: true
+ });
+
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "enable",
+ ];
+ log_cmd("enableSecurity", "Enable security", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.addNotification(
+ "success",
+ `Successfully enabled security. You must restart the server for this to take effect.`
+ );
+ this.setState({
+ securityEnabled: true,
+ secEnableSpinner: false,
+ showSecurityEnableModal: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error enabling security - ${msg}`
+ );
+ this.setState({
+ secEnableSpinner: false,
+ showSecurityEnableModal: false,
+ });
+ });
+ }
+
+ disableSecurity () {
+ const cmd = [
+ "dsconf", "-j", "ldapi://%2fvar%2frun%2fslapd-" + this.props.serverId + ".socket",
+ "security", "disable",
+ ];
+ log_cmd("disableSecurity", "Disable security", cmd);
+ cockpit
+ .spawn(cmd, { superuser: true, err: "message" })
+ .done(() => {
+ this.addNotification(
+ "success",
+ `Successfully disabled security. You must restart the server for this to take effect.`
+ );
+ this.setState({
+ securityEnabled: false,
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error disabling security - ${msg}`
+ );
+ });
+ }
+
+ saveSecurityConfig () {
+ let cmd = [
+ 'dsconf', '-j', 'ldapi://%2fvar%2frun%2fslapd-' + this.props.serverId + '.socket',
+ 'security', 'set'
+ ];
+
+ if (this.state._validateCert != this.state.validateCert) {
+ cmd.push("--verify-cert-chain-on-startup=" + this.state.validateCert);
+ }
+ if (this.state._sslVersionMin != this.state.sslVersionMin) {
+ cmd.push("--tls-protocol-min=" + this.state.sslVersionMin);
+ }
+ if (this.state._sslVersionMax != this.state.sslVersionMax) {
+ cmd.push("--tls-protocol-max=" + this.state.sslVersionMax);
+ }
+ if (this.state._clientAuth != this.state.clientAuth) {
+ cmd.push("--tls-client-auth=" + this.state.clientAuth);
+ }
+ if (this.state._securePort != this.state.securePort) {
+ cmd.push("--secure-port=" + this.state.securePort);
+ }
+ if (this.state._secureListenhost != this.state.secureListenhost) {
+ cmd.push("--listen-host=" + this.state.secureListenhost);
+ }
+ if (this.state._allowWeakCipher != this.state.allowWeakCipher) {
+ let val = "off";
+ if (this.state.allowWeakCipher) {
+ val = "on";
+ }
+ cmd.push("--allow-insecure-ciphers=" + val);
+ }
+ if (this.state._checkHostname != this.state.checkHostname) {
+ let val = "off";
+ if (this.state.checkHostname) {
+ val = "on";
+ }
+ cmd.push("--check-hostname=" + val);
+ }
+ if (this.state._requireSecureBinds != this.state.requireSecureBinds) {
+ let val = "off";
+ if (this.state.requireSecureBinds) {
+ val = "on";
+ }
+ cmd.push("--require-secure-authentication=" + val);
+ }
+
+ if (cmd.length > 5) {
+ log_cmd("saveSecurityConfig", "Applying security config change", cmd);
+ let msg = "Successfully updated security configuration. You must restart the server for these changes to take effect.";
+
+ this.setState({
+ // Start the spinner
+ saving: true
+ });
+
+ cockpit
+ .spawn(cmd, {superuser: true, "err": "message"})
+ .done(content => {
+ this.loadSecurityConfig(1);
+ this.addNotification(
+ "success",
+ msg
+ );
+ this.setState({
+ saving: false
+ });
+ })
+ .fail(err => {
+ let errMsg = JSON.parse(err);
+ this.loadSecurityConfig();
+ this.setState({
+ saving: false
+ });
+ let msg = errMsg.desc;
+ if ('info' in errMsg) {
+ msg = errMsg.desc + " - " + errMsg.info;
+ }
+ this.addNotification(
+ "error",
+ `Error updating security configuration - ${msg}`
+ );
+ });
+ }
+ }
+
+ handleTypeaheadChange(value) {
+ if (value.length == 0) {
+ return;
+ }
+ this.setState({
+ nssslpersonalityssl: value[0],
+ });
+ }
+
+ handleChange(e) {
+ const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
+ this.setState({
+ [e.target.id]: value,
+ });
+ }
+
+ handleLoginModal(e) {
+ const value = e.target.value.trim();
+ let valueErr = false;
+ let errObj = this.state.errObj;
+ if (value == "") {
+ valueErr = true;
+ }
+ errObj[e.target.id] = valueErr;
+ this.setState({
+ [e.target.id]: value,
+ errObj: errObj
+ });
+ }
+
+ render() {
+ let securityPage = "";
+ let serverCert = [this.state.nssslpersonalityssl];
+
+ if (this.state.loaded && !this.state.saving) {
+ let configPage = "";
+ if (this.state.securityEnabled) {
+ configPage =
+ <div>
+ <Row className="ds-margin-top" title="The server's secure port number (nsslapd-secureport).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Server Secure Port
+ </Col>
+ <Col sm={4}>
+ <input id="securePort" className="ds-input-auto" onChange={this.handleChange} type="text" defaultValue={this.state.securePort} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="This parameter can be used to restrict the Directory Server instance to a single IP interface (hostname, or IP address). This parameter specifically sets what interface to use for TLS traffic. Requires restart. (nsslapd-securelistenhost).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Secure Listen Host
+ </Col>
+ <Col sm={4}>
+ <input id="secureListenhost" className="ds-input-auto" type="text" onChange={this.handleChange} defaultValue={this.state.secureListenhost} />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The name, or nickname, of the server certificate inthe NSS datgabase the server should use (nsSSLPersonalitySSL).">
+ <Col className="ds-no-padding" sm={2}>
+ <ControlLabel>Server Certificate Name</ControlLabel>
+ </Col>
+ <Col sm={4}>
+ <Typeahead
+ id="serverCertNameTypeahead"
+ onChange={this.handleTypeaheadChange}
+ selected={serverCert}
+ emptyLabel="No matching certificates found"
+ options={this.state.serverCertNames}
+ newSelectionPrefix="Select a server certificate"
+ placeholder="Type a sever certificate nickname..."
+ />
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The minimum SSL/TLS version the server will accept (sslversionmin).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Minimum TLS Version
+ </Col>
+ <Col sm={4}>
+ <select id="sslVersionMin" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.sslVersionMin}>
+ <option />
+ <option>TLS1.3</option>
+ <option>TLS1.2</option>
+ <option>TLS1.1</option>
+ <option>TLS1.0</option>
+ <option>SSL3</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="The maximum SSL/TLS version the server will accept (sslversionmax).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Maximum TLS Version
+ </Col>
+ <Col sm={4}>
+ <select id="sslVersionMax" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.sslVersionMax}>
+ <option />
+ <option>TLS1.3</option>
+ <option>TLS1.2</option>
+ <option>TLS1.1</option>
+ <option>TLS1.0</option>
+ <option>SSL3</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Sets how the Directory Server enforces TLS client authentication (nsSSLClientAuth).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Client Authentication
+ </Col>
+ <Col sm={4}>
+ <select id="clientAuth" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.clientAuth}>
+ <option>off</option>
+ <option>allowed</option>
+ <option>required</option>
+ </select>
+ </Col>
+ </Row>
+ <Row className="ds-margin-top" title="Validate server's certificate expiration date (nsslapd-validate-cert).">
+ <Col componentClass={ControlLabel} sm={2}>
+ Validate Certificate
+ </Col>
+ <Col sm={4}>
+ <select id="validateCert" className="btn btn-default dropdown ds-select" onChange={this.handleChange} defaultValue={this.state.validateCert}>
+ <option>warn</option>
+ <option>on</option>
+ <option>off</option>
+ </select>
+ </Col>
+ </Row>
+ <p />
+ <Row>
+ <Col sm={5}>
+ <Checkbox
+ id="requireSecureBinds"
+ defaultChecked={this.state.requireSecureBinds}
+ onChange={this.handleChange}
+ title="Require all connections use TLS (nsslapd-require-secure-binds)."
+ >
+ Require Secure Connections
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={5}>
+ <Checkbox
+ id="checkHostname"
+ defaultChecked={this.state.checkHostname}
+ onChange={this.handleChange}
+ title="Verify authenticity of a request by matching the host name against the value assigned to the common name (cn) attribute of the subject name (subjectDN field) in the certificate being presented. (nsslapd-ssl-check-hostname)."
+ >
+ Verify Certificate Subject Hostname
+ </Checkbox>
+ </Col>
+ </Row>
+ <Row>
+ <Col sm={5}>
+ <Checkbox
+ id="allowWeakCipher"
+ defaultChecked={this.state.allowWeakCipher}
+ onChange={this.handleChange}
+ title="Allow weak ciphers (allowWeakCipher)."
+ >
+ Allow Weak Ciphers
+ </Checkbox>
+ </Col>
+ </Row>
+ <p />
+ <Button
+ bsStyle="primary"
+ className="ds-margin-top-med"
+ onClick={() => {
+ this.saveSecurityConfig();
+ }}
+ >
+ Save Configuration
+ </Button>
+ </div>;
+ }
+
+ securityPage =
+ <div className="container-fluid">
+ <NotificationController
+ notifications={this.state.notifications}
+ removeNotificationAction={this.removeNotification}
+ />
+ <div className="ds-tab-table">
+ <TabContainer id="basic-tabs-pf" onSelect={this.handleNavSelect} activeKey={this.state.activeKey}>
+ <div>
+ <Nav bsClass="nav nav-tabs nav-tabs-pf">
+ <NavItem eventKey={1}>
+ <div dangerouslySetInnerHTML={{__html: 'Security Configuration'}} />
+ </NavItem>
+ <NavItem eventKey={2}>
+ <div dangerouslySetInnerHTML={{__html: 'Certificate Management'}} />
+ </NavItem>
+ <NavItem eventKey={3}>
+ <div dangerouslySetInnerHTML={{__html: 'Cipher Preferences'}} />
+ </NavItem>
+ </Nav>
+ <TabContent>
+ <TabPane eventKey={1}>
+ <div className="ds-margin-top-xlg ds-indent">
+ <Row>
+ <Col componentClass={ControlLabel} sm={2}>
+ Security Enabled
+ </Col>
+ <Col sm={2}>
+ <Switch
+ onChange={this.handleSwitchChange}
+ checked={this.state.securityEnabled}
+ height={20}
+ />
+ </Col>
+ </Row>
+ <hr />
+ {configPage}
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={2}>
+ <div className="ds-margin-top-lg">
+ <CertificateManagement
+ serverId={this.props.serverId}
+ CACerts={this.state.CACerts}
+ ServerCerts={this.state.serverCerts}
+ addNotification={this.addNotification}
+ />
+ </div>
+ </TabPane>
+
+ <TabPane eventKey={3}>
+ <div className="ds-indent ds-tab-table">
+ <Ciphers
+ serverId={this.props.serverId}
+ supportedCiphers={this.state.supportedCiphers}
+ cipherPref={this.state.cipherPref}
+ enabledCiphers={this.state.enabledCiphers}
+ addNotification={this.addNotification}
+ />
+ </div>
+ </TabPane>
+ </TabContent>
+ </div>
+ </TabContainer>
+ </div>
+ </div>;
+ } else if (this.state.saving) {
+ securityPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Saving security information ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ } else {
+ securityPage =
+ <div className="ds-loading-spinner ds-center">
+ <p />
+ <h4>Loading security information ...</h4>
+ <Spinner loading size="md" />
+ </div>;
+ }
+ return (
+ <div>
+ {securityPage}
+ <ConfirmPopup
+ showModal={this.state.showConfirmDisable}
+ closeHandler={this.closeConfirmDisable}
+ actionFunc={this.disableSecurity}
+ msg="Are you sure you want to disable security?"
+ msgContent="Attention: this requires the server to be restarted to take effect."
+ />
+ <SecurityEnableModal
+ showModal={this.state.showSecurityEnableModal}
+ closeHandler={this.closeSecurityEnableModal}
+ handleChange={this.handleSecEnableChange}
+ saveHandler={this.enableSecurity}
+ primaryName={this.state.primaryCertName}
+ certs={this.state.serverCerts}
+ spinning={this.state.secEnableSpinner}
+ />
+ </div>
+ );
+ }
+}
+
+// Props and defaultProps
+
+Security.propTypes = {
+ serverId: PropTypes.string,
+};
+
+Security.defaultProps = {
+ serverId: "",
+};
+
+export default Security;
diff --git a/src/cockpit/389-console/webpack.config.js b/src/cockpit/389-console/webpack.config.js
index 8c0b433..941a6e8 100644
--- a/src/cockpit/389-console/webpack.config.js
+++ b/src/cockpit/389-console/webpack.config.js
@@ -34,8 +34,6 @@ var info = {
"replication.js",
"schema.html",
"schema.js",
- "security.html",
- "security.js",
"servers.html",
"servers.js",
"static",
@@ -131,7 +129,18 @@ module.exports = {
{
exclude: /node_modules/,
loader: "babel-loader",
- test: /\.jsx$/
+ test: /\.jsx$/,
+ options: {
+ presets: [
+ '@babel/preset-env',
+ '@babel/preset-react',
+ {
+ plugins: [
+ '@babel/plugin-proposal-class-properties'
+ ]
+ }
+ ]
+ },
},
{
exclude: /node_modules/,
diff --git a/src/lib389/cli/dsconf b/src/lib389/cli/dsconf
index c0c0b4d..22635ca 100755
--- a/src/lib389/cli/dsconf
+++ b/src/lib389/cli/dsconf
@@ -11,14 +11,11 @@
# PYTHON_ARGCOMPLETE_OK
import argparse, argcomplete
-import logging
import ldap
import sys
import signal
import json
import ast
-from lib389 import DirSrv
-from lib389._constants import DN_CONFIG, DN_DM
from lib389.cli_conf import config as cli_config
from lib389.cli_conf import backend as cli_backend
from lib389.cli_conf import directory_manager as cli_directory_manager
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
index c7324f1..6e2e54a 100644
--- a/src/lib389/lib389/__init__.py
+++ b/src/lib389/lib389/__init__.py
@@ -1,5 +1,5 @@
# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
+# Copyright (C) 2019 Red Hat, Inc.
# Copyright (C) 2019 William Brown <william(a)blackhats.net.au>
# All rights reserved.
#
@@ -19,40 +19,28 @@
TODO: reorganize method parameters according to SimpleLDAPObject
naming: filterstr, attrlist
"""
-try:
- from subprocess import Popen, PIPE, STDOUT
- HASPOPEN = True
-except ImportError:
- import popen2
- HASPOPEN = False
-
-import io
+
import sys
import os
import stat
import pwd
import grp
import os.path
-import base64
import socket
import ldif
import re
import ldap
import ldapurl
import time
-import operator
import shutil
from datetime import datetime
import logging
-import decimal
import glob
import tarfile
import subprocess
from collections.abc import Callable
import signal
import errno
-import pwd
-import grp
import uuid
import json
from shutil import copy2
@@ -63,25 +51,18 @@ import warnings
import inspect
from ldap.ldapobject import SimpleLDAPObject
-from ldap.cidict import cidict
-from ldap import LDAPError
# file in this package
from lib389._constants import *
from lib389.properties import *
from lib389._entry import Entry
-from lib389._replication import CSN, RUV
from lib389._ldifconn import LDIFConn
from lib389.tools import DirSrvTools
-from lib389.mit_krb5 import MitKrb5
from lib389.utils import (
ds_is_older,
isLocalHost,
- is_a_dn,
normalizeDN,
- suffixfilt,
escapeDNValue,
- update_newhost_with_fqdn,
formatInfData,
ensure_bytes,
ensure_str,
@@ -765,7 +746,7 @@ class DirSrv(SimpleLDAPObject, object):
for pi in potential_inst:
pi_dse_ldif = os.path.join(pi, 'dse.ldif')
# Takes /etc/dirsrv/slapd-instance -> slapd-instance -> instance
- pi_name = pi.split('/')[-1].split('-')[-1]
+ pi_name = pi.split('/')[-1].split('slapd-')[-1]
# parse + append
if os.path.exists(pi_dse_ldif):
instances.append(_parse_configfile(pi_dse_ldif, pi_name))
@@ -3094,7 +3075,7 @@ class DirSrv(SimpleLDAPObject, object):
]
try:
- result = subprocess.check_output(cmd, encoding='utf-8')
+ subprocess.check_output(cmd, encoding='utf-8')
except subprocess.CalledProcessError as e:
self.log.debug("Command: %s failed with the return code %s and the error %s",
format_cmd_list(cmd), e.returncode, e.output)
diff --git a/src/lib389/lib389/cli_conf/security.py b/src/lib389/lib389/cli_conf/security.py
index 6d8c1ae..20f2574 100644
--- a/src/lib389/lib389/cli_conf/security.py
+++ b/src/lib389/lib389/cli_conf/security.py
@@ -8,7 +8,7 @@
from collections import OrderedDict, namedtuple
import json
-
+import os
from lib389.config import Config, Encryption, RSA
from lib389.nss_ssl import NssSsl
@@ -27,45 +27,48 @@ SECURITY_ATTRS_MAP = OrderedDict([
('secure-port', Props(Config, 'nsslapd-securePort',
'Port for LDAPS to listen on',
range(1, 65536))),
- ('tls-client-auth', Props(Config, 'nsSSLClientAuth',
- 'Client authentication requirement',
- ('off', 'allowed', 'required'))),
+ ('tls-client-auth', Props(Encryption, 'nsSSLClientAuth',
+ 'Client authentication requirement',
+ ('off', 'allowed', 'required'))),
('require-secure-authentication', Props(Config, 'nsslapd-require-secure-binds',
- 'Require binds over LDAPS, StartTLS, or SASL',
- onoff)),
+ 'Require binds over LDAPS, StartTLS, or SASL',
+ onoff)),
('check-hostname', Props(Config, 'nsslapd-ssl-check-hostname',
'Check Subject of remote certificate against the hostname',
onoff)),
('verify-cert-chain-on-startup', Props(Config, 'nsslapd-validate-cert',
- 'Validate server certificate during startup',
- ('warn', *onoff))),
+ 'Validate server certificate during startup',
+ ('warn', *onoff))),
('session-timeout', Props(Encryption, 'nsSSLSessionTimeout',
'Secure session timeout',
int)),
('tls-protocol-min', Props(Encryption, 'sslVersionMin',
- 'Secure protocol minimal allowed version',
- protocol_versions)),
+ 'Secure protocol minimal allowed version',
+ protocol_versions)),
('tls-protocol-max', Props(Encryption, 'sslVersionMax',
- 'Secure protocol maximal allowed version',
- protocol_versions)),
+ 'Secure protocol maximal allowed version',
+ protocol_versions)),
('allow-insecure-ciphers', Props(Encryption, 'allowWeakCipher',
- 'Allow weak ciphers for legacy use',
- onoff)),
+ 'Allow weak ciphers for legacy use',
+ onoff)),
('allow-weak-dh-param', Props(Encryption, 'allowWeakDHParam',
'Allow short DH params for legacy use',
onoff)),
+ ('cipher-pref', Props(Encryption, 'nsSSL3Ciphers',
+ 'Use this command to directly set nsSSL3Ciphers attribute. It is a comma separated list '
+ 'of cipher names (prefixed with + or -), optionally including +all or -all. The attribute '
+ 'may optionally be prefixed by keyword default. Please refer to documentation of '
+ 'the attribute for a more detailed description.',
+ onoff)),
])
RSA_ATTRS_MAP = OrderedDict([
('tls-allow-rsa-certificates', Props(RSA, 'nsSSLActivation',
- 'Activate use of RSA certificates',
- onoff)),
+ 'Activate use of RSA certificates', onoff)),
('nss-cert-name', Props(RSA, 'nsSSLPersonalitySSL',
- 'Server certificate name in NSS DB',
- str)),
+ 'Server certificate name in NSS DB', str)),
('nss-token', Props(RSA, 'nsSSLToken',
- 'Security token name (module of NSS DB)',
- str))
+ 'Security token name (module of NSS DB)', str))
])
@@ -73,7 +76,9 @@ def _security_generic_get(inst, basedn, logs, args, attrs_map):
result = {}
for attr, props in attrs_map.items():
val = props.cls(inst).get_attr_val_utf8(props.attr)
- result[props.attr] = val
+ if val is None:
+ val = ""
+ result[props.attr.lower()] = val
if args.json:
print(json.dumps({'type': 'list', 'items': result}))
else:
@@ -126,14 +131,22 @@ def _security_generic_toggle_parsers(parent, cls, attr, help_pattern):
return list(map(add_parser, ('Enable', 'Disable'), ('on', 'off')))
-
def security_enable(inst, basedn, log, args):
dbpath = inst.get_cert_dir()
tlsdb = NssSsl(dbpath=dbpath)
- if not tlsdb._db_exists(even_partial=True): # we want to be very careful
- log.info(f'Secure database does not exist. Creating a new one in {dbpath}.')
- tlsdb.reinit()
-
+ certs = tlsdb.list_certs()
+ if len(certs) == 0:
+ raise ValueError('There are no server certificates in the security ' +
+ 'database, security can not be enabled.')
+
+ if len(certs) == 1:
+ # If there is only cert make sure it is set as the server certificate
+ RSA(inst).set('nsSSLPersonalitySSL', certs[0][0])
+ elif args.cert_name is not None:
+ # A certificate nickname was provided, set it as the server certificate
+ RSA(inst).set('nsSSLPersonalitySSL', args.cert_name)
+
+ # it should now be safe to enable security
Config(inst).set('nsslapd-security', 'on')
@@ -184,29 +197,246 @@ def security_ciphers_list(inst, basedn, log, args):
print(*lst, sep='\n')
+def cert_add(inst, basedn, log, args):
+ """Add server certificate
+ """
+ # Verify file and certificate name
+ os.path.isfile(args.file)
+ tlsdb = NssSsl(dirsrv=inst)
+ if not tlsdb._db_exists(even_partial=True): # we want to be very careful
+ log.info('Security database does not exist. Creating a new one in {}.'.format(inst.get_cert_dir()))
+ tlsdb.reinit()
+
+ try:
+ tlsdb.get_cert_details(args.name)
+ raise ValueError("Certificate already exists with the same name")
+ except ValueError:
+ pass
+
+ if args.primary_cert:
+ # This is the server's primary certificate, update RSA entry
+ RSA(inst).set('nsSSLPersonalitySSL', args.name)
+
+ # Add the cert
+ tlsdb.add_cert(args.name, args.file)
+
+
+def cacert_add(inst, basedn, log, args):
+ """Add CA certificate
+ """
+ # Verify file and certificate name
+ os.path.isfile(args.file)
+ tlsdb = NssSsl(dirsrv=inst)
+ if not tlsdb._db_exists(even_partial=True): # we want to be very careful
+ log.info('Security database does not exist. Creating a new one in {}.'.format(inst.get_cert_dir()))
+ tlsdb.reinit()
+
+ try:
+ tlsdb.get_cert_details(args.name)
+ raise ValueError("Certificate already exists with the same name")
+ except ValueError:
+ pass
+
+ # Add the cert
+ tlsdb.add_cert(args.name, args.file, ca=True)
+
+
+def cert_list(inst, basedn, log, args):
+ """List all the server certificates
+ """
+ cert_list = []
+ tlsdb = NssSsl(dirsrv=inst)
+ certs = tlsdb.list_certs()
+ for cert in certs:
+ if args.json:
+ cert_list.append(
+ {
+ "type": "certificate",
+ "attrs": {
+ 'nickname': cert[0],
+ 'subject': cert[1],
+ 'issuer': cert[2],
+ 'expires': cert[3],
+ 'flags': cert[4],
+ }
+ }
+ )
+ else:
+ log.info('Certificate Name: {}'.format(cert[0]))
+ log.info('Subject DN: {}'.format(cert[1]))
+ log.info('Issuer DN: {}'.format(cert[2]))
+ log.info('Expires: {}'.format(cert[3]))
+ log.info('Trust Flags: {}\n'.format(cert[4]))
+ if args.json:
+ log.info(json.dumps(cert_list))
+
+
+def cacert_list(inst, basedn, log, args):
+ """List all CA certs
+ """
+ cert_list = []
+ tlsdb = NssSsl(dirsrv=inst)
+ certs = tlsdb.list_certs(ca=True)
+ for cert in certs:
+ if args.json:
+ cert_list.append(
+ {
+ "type": "certificate",
+ "attrs": {
+ 'nickname': cert[0],
+ 'subject': cert[1],
+ 'issuer': cert[2],
+ 'expires': cert[3],
+ 'flags': cert[4],
+ }
+ }
+ )
+ else:
+ log.info('Certificate Name: {}'.format(cert[0]))
+ log.info('Subject DN: {}'.format(cert[1]))
+ log.info('Issuer DN: {}'.format(cert[2]))
+ log.info('Expires: {}'.format(cert[3]))
+ log.info('Trust Flags: {}\n'.format(cert[4]))
+ if args.json:
+ log.info(json.dumps(cert_list))
+
+
+def cert_get(inst, basedn, log, args):
+ """Get the details about a server certificate
+ """
+ tlsdb = NssSsl(dirsrv=inst)
+ details = tlsdb.get_cert_details(args.name)
+ if args.json:
+ log.info(json.dumps(
+ {
+ "type": "certificate",
+ "attrs": {
+ 'nickname': details[0],
+ 'subject': details[1],
+ 'issuer': details[2],
+ 'expires': details[3],
+ 'flags': details[4],
+ }
+ }
+ )
+ )
+ else:
+ log.info('Certificate Name: {}'.format(details[0]))
+ log.info('Subject DN: {}'.format(details[1]))
+ log.info('Issuer DN: {}'.format(details[2]))
+ log.info('Expires: {}'.format(details[3]))
+ log.info('Trust Flags: {}'.format(details[4]))
+
+
+def cert_edit(inst, basedn, log, args):
+ """Edit cert
+ """
+ tlsdb = NssSsl(dirsrv=inst)
+ tlsdb.edit_cert_trust(args.name, args.flags)
+
+
+def cert_del(inst, basedn, log, args):
+ """Delete cert
+ """
+ tlsdb = NssSsl(dirsrv=inst)
+ tlsdb.del_cert(args.name)
+
+
def create_parser(subparsers):
security = subparsers.add_parser('security', help='Query and manipulate security options')
security_sub = security.add_subparsers(help='security')
- security_set = _security_generic_set_parser(security_sub, SECURITY_ATTRS_MAP, 'Set general security options',
+
+ # Core security management
+ _security_generic_set_parser(security_sub, SECURITY_ATTRS_MAP, 'Set general security options',
('Use this command for setting security related options located in cn=config and cn=encryption,cn=config.'
'\n\nTo enable/disable security you can use enable and disable commands instead.'))
- security_get = _security_generic_get_parser(security_sub, SECURITY_ATTRS_MAP, 'Get general security options')
+ _security_generic_get_parser(security_sub, SECURITY_ATTRS_MAP, 'Get general security options')
security_enable_p = security_sub.add_parser('enable', help='Enable security', description=(
'If missing, create security database, then turn on security functionality. Please note this is usually not'
- ' enought for TLS connections to work - proper setup of CA and server certificate is necessary.'))
+ ' enough for TLS connections to work - proper setup of CA and server certificate is necessary.'))
+ security_enable_p.add_argument('--cert-name', default=None,
+ help='The name of the certificate the server should use')
security_enable_p.set_defaults(func=security_enable)
security_disable_p = security_sub.add_parser('disable', help='Disable security', description=(
'Turn off security functionality. The rest of the configuration will be left untouched.'))
security_disable_p.set_defaults(func=security_disable)
- rsa = security_sub.add_parser('rsa', help='Query and mainpulate RSA security options')
+ # Server certificate management
+ certs = security_sub.add_parser('certificate', help='Manage TLS certificates')
+ certs_sub = certs.add_subparsers(help='certificate')
+ cert_add_parser = certs_sub.add_parser('add', help='Add a server certificate', description=(
+ 'Add a server certificate to the NSS database'))
+ cert_add_parser.add_argument('--file', required=True,
+ help='The file name of the certificate')
+ cert_add_parser.add_argument('--name', required=True,
+ help='The name/nickname of the certificate')
+ cert_add_parser.add_argument('--primary-cert', action='store_true',
+ help="Set this certificate as the server's certificate")
+ cert_add_parser.set_defaults(func=cert_add)
+
+ cert_edit_parser = certs_sub.add_parser('set-trust-flags', help='Set the Trust flags',
+ description=('Change the trust flags of a server certificate'))
+ cert_edit_parser.add_argument('name', help='The name/nickname of the certificate')
+ cert_edit_parser.add_argument('--flags', required=True,
+ help='The trust flags for the server certificate')
+ cert_edit_parser.set_defaults(func=cert_edit)
+
+ cert_del_parser = certs_sub.add_parser('del', help='Delete a certificate',
+ description=('Delete a certificate from the NSS database'))
+ cert_del_parser.add_argument('name', help='The name/nickname of the certificate')
+ cert_del_parser.set_defaults(func=cert_del)
+
+ cert_get_parser = certs_sub.add_parser('get', help="Get a server certificate's information",
+ description=('Get detailed information about a certificate, like trust attributes, expiration dates, Subject and Issuer DNs '))
+ cert_get_parser.add_argument('name', help='The name/nickname of the certificate')
+ cert_get_parser.set_defaults(func=cert_get)
+
+ cert_list_parser = certs_sub.add_parser('list', help='List the server certificates',
+ description=('List the server certificates in the NSS database'))
+ cert_list_parser.set_defaults(func=cert_list)
+
+ # CA certificate management
+ cacerts = security_sub.add_parser('ca-certificate', help='Manage TLS Certificate Authorities')
+ cacerts_sub = cacerts.add_subparsers(help='ca-certificate')
+ cacert_add_parser = cacerts_sub.add_parser('add', help='Add a Certificate Authority', description=(
+ 'Add a Certificate Authority to the NSS database'))
+ cacert_add_parser.add_argument('--file', required=True,
+ help='The file name of the CA certificate')
+ cacert_add_parser.add_argument('--name', required=True,
+ help='The name/nickname of the CA certificate')
+ cacert_add_parser.set_defaults(func=cacert_add)
+
+ cacert_edit_parser = cacerts_sub.add_parser('set-trust-flags', help='Set the Trust flags',
+ description=('Change the trust attributes of a CA certificate. Certificate Authorities typically use "CT,,"'))
+ cacert_edit_parser.add_argument('name', help='The name/nickname of the CA certificate')
+ cacert_edit_parser.add_argument('--flags', required=True,
+ help='The trust flags for the CA certificate')
+ cacert_edit_parser.set_defaults(func=cert_edit)
+
+ cacert_del_parser = cacerts_sub.add_parser('del', help='Delete a certificate',
+ description=('Delete a CA certificate from the NSS database'))
+ cacert_del_parser.add_argument('name', help='The name/nickname of the CA certificate')
+ cacert_del_parser.set_defaults(func=cert_del)
+
+ cacert_get_parser = cacerts_sub.add_parser('get', help="Get a Certificate Authority's information",
+ description=('Get detailed information about a CA certificate, like trust attributes, expiration dates, Subject and Issuer DN'))
+ cacert_get_parser.add_argument('name', help='The name/nickname of the CA certificate')
+ cacert_get_parser.set_defaults(func=cert_get)
+
+ cacert_list_parser = cacerts_sub.add_parser('list', help='List the Certificate Authorities',
+ description=('List the CA certificates in the NSS database'))
+ cacert_list_parser.set_defaults(func=cacert_list)
+
+ # RSA management
+ rsa = security_sub.add_parser('rsa', help='Query and manipulate RSA security options')
rsa_sub = rsa.add_subparsers(help='rsa')
- rsa_set = _security_generic_set_parser(rsa_sub, RSA_ATTRS_MAP, 'Set RSA security options',
+ _security_generic_set_parser(rsa_sub, RSA_ATTRS_MAP, 'Set RSA security options',
('Use this command for setting RSA (private key) related options located in cn=RSA,cn=encryption,cn=config.'
'\n\nTo enable/disable RSA you can use enable and disable commands instead.'))
- rsa_get = _security_generic_get_parser(rsa_sub, RSA_ATTRS_MAP, 'Get RSA security options')
- rsa_toggles = _security_generic_toggle_parsers(rsa_sub, RSA, 'nsSSLActivation', '{} RSA')
+ _security_generic_get_parser(rsa_sub, RSA_ATTRS_MAP, 'Get RSA security options')
+ _security_generic_toggle_parsers(rsa_sub, RSA, 'nsSSLActivation', '{} RSA')
+ # Cipher management
ciphers = security_sub.add_parser('ciphers', help='Manage secure ciphers')
ciphers_sub = ciphers.add_subparsers(help='ciphers')
@@ -226,7 +456,7 @@ def create_parser(subparsers):
ciphers_set = ciphers_sub.add_parser('set', help='Set ciphers attribute', description=(
'Use this command to directly set nsSSL3Ciphers attribute. It is a comma separated list '
- 'of cipher names (prefixed with + or -), optionaly including +all or -all. The attribute '
+ 'of cipher names (prefixed with + or -), optionally including +all or -all. The attribute '
'may optionally be prefixed by keyword default. Please refer to documentation of '
'the attribute for a more detailed description.'))
ciphers_set.set_defaults(func=security_ciphers_set)
diff --git a/src/lib389/lib389/config.py b/src/lib389/lib389/config.py
index c2a34fa..23ab9f2 100644
--- a/src/lib389/lib389/config.py
+++ b/src/lib389/lib389/config.py
@@ -20,9 +20,7 @@ import ldap
from lib389._constants import *
from lib389 import Entry
from lib389._mapped_object import DSLdapObject
-from lib389.dseldif import DSEldif
-from lib389.utils import ensure_bytes, ensure_str
-
+from lib389.utils import ensure_bytes, selinux_label_port, selinux_present
from lib389.lint import DSCLE0001, DSCLE0002, DSELE0001
class Config(DSLdapObject):
@@ -37,7 +35,7 @@ class Config(DSLdapObject):
super(Config, self).__init__(instance=conn)
self._dn = DN_CONFIG
# self._instance = conn
- # self.log = conn.log
+ self.log = conn.log
config_compare_exclude = [
'nsslapd-ldapifilepath',
'nsslapd-accesslog',
@@ -65,6 +63,16 @@ class Config(DSLdapObject):
def rdn(self):
return DN_CONFIG
+ def replace(self, key, value):
+ if key.lower() == 'nsslapd-secureport' and selinux_present():
+ # Get old port and remove label
+ old_port = self.get_attr_val_utf8(key)
+ self.log.debug("Removing old port's selinux label...")
+ selinux_label_port(old_port, remove_label=True)
+ self.log.debug("Setting new port's selinux label...")
+ selinux_label_port(value)
+ super(Config, self).replace(key, value)
+
def _alter_log_enabled(self, service, state):
if service not in ('access', 'error', 'audit'):
self._log.error('Attempted to enable invalid log service "%s"' % service)
@@ -245,7 +253,10 @@ class Encryption(DSLdapObject):
:returns: list of str
"""
val = self.get_attr_val_utf8('nsSSL3Ciphers')
- return val.split(',') if val else []
+ if val:
+ return val.split(',')
+ else:
+ return ['default']
@ciphers.setter
def ciphers(self, ciphers):
@@ -370,7 +381,7 @@ class CertmapLegacy(object):
def _parse_maps(self, maps):
certmaps = {}
- cur_map = None
+
for l in maps:
if l.startswith('certmap'):
# Line matches format of: certmap name issuer
@@ -457,10 +468,7 @@ class LDBMConfig(DSLdapObject):
def __init__(self, conn):
super(LDBMConfig, self).__init__(instance=conn)
self._dn = DN_CONFIG_LDBM
- config_compare_exclude = []
+ # config_compare_exclude = []
self._rdn_attribute = 'cn'
self._lint_functions = []
self._protected = True
-
-
-
diff --git a/src/lib389/lib389/nss_ssl.py b/src/lib389/lib389/nss_ssl.py
index a54095c..8af7132 100644
--- a/src/lib389/lib389/nss_ssl.py
+++ b/src/lib389/lib389/nss_ssl.py
@@ -10,9 +10,6 @@
"""
import os
-import sys
-import random
-import string
import re
import socket
import time
@@ -24,7 +21,7 @@ from datetime import datetime, timedelta
from subprocess import check_output
from lib389.passwd import password_generate
-from lib389.utils import ensure_str, ensure_bytes, format_cmd_list
+from lib389.utils import ensure_str, format_cmd_list
import uuid
KEYBITS = 4096
@@ -362,8 +359,9 @@ only.
# Now make the lines usable
cert_values = []
for line in lines:
- data = line.split()
- cert_values.append((data[0], data[1]))
+ if line == '':
+ continue
+ cert_values.append(re.match(r'^(.+[^\s])[\s]+([^\s]+)$', line.rstrip()).groups())
return cert_values
def _rsa_cert_key_exists(self, cert_tuple):
@@ -380,7 +378,6 @@ only.
result = ensure_str(check_output(cmd, stderr=subprocess.STDOUT))
lines = result.split('\n')[1:-1]
- key_list = []
for line in lines:
m = re.match('\<(?P<id>.*)\> (?P<type>\w+)\s+(?P<hash>\w+).*:(?P<name>.+)', line)
if name == m.group('name'):
@@ -712,3 +709,148 @@ only.
crt_der_path = '%s/%s%s.der' % (self._certdb, USER_PREFIX, name)
return {'ca': ca_path, 'key': key_path, 'crt': crt_path, 'crt_der_path': crt_der_path}
+ # Certificate helper functions
+ def del_cert(self, nickname):
+ """Delete this certificate
+ """
+ cmd = [
+ '/usr/bin/certutil',
+ '-D',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("del_cert cmd: %s", format_cmd_list(cmd))
+ check_output(cmd, stderr=subprocess.STDOUT)
+
+ def edit_cert_trust(self, nickname, trust_flags):
+ """Edit trust flags
+ """
+
+ # validate trust flags
+ flag_sections = trust_flags.split(',')
+ if len(flag_sections) != 3:
+ raise ValueError("Invalid trust flag format")
+
+ for section in flag_sections:
+ if len(section) > 6:
+ raise ValueError("Invalid trust flag format, too many flags in a section")
+
+ for c in trust_flags:
+ if c not in ['p', 'P', 'c', 'C', 'T', 'u', ',']:
+ raise ValueError("Invalid trust flag {}".format(c))
+
+ # Modify certificate flags
+ cmd = [
+ '/usr/bin/certutil',
+ '-M',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-t', trust_flags,
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("edit_cert_trust cmd: %s", format_cmd_list(cmd))
+ check_output(cmd, stderr=subprocess.STDOUT)
+
+
+ def get_cert_details(self, nickname):
+ """Get the trust flags, subject DN, issuer, and expiration date
+
+ return a list:
+ 0 - nickname
+ 1 - subject
+ 2 - issuer
+ 3 - expire date
+ 4 - trust_flags
+ """
+ all_certs = self._rsa_cert_list()
+ for cert in all_certs:
+ if cert[0] == nickname:
+ trust_flags = cert[1]
+ cmd = [
+ '/usr/bin/certutil',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-L',
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("get_cert_details cmd: %s", format_cmd_list(cmd))
+
+ # Expiration date
+ certdetails = check_output(cmd, stderr=subprocess.STDOUT, encoding='utf-8')
+ end_date_str = certdetails.split("Not After : ")[1].split("\n")[0]
+ date_format = '%a %b %d %H:%M:%S %Y'
+ end_date = datetime.strptime(end_date_str, date_format)
+
+ # Subject DN
+ subject = ""
+ for line in certdetails.splitlines():
+ line = line.lstrip()
+ if line.startswith("Subject: "):
+ subject = line.split("Subject: ")[1].split("\n")[0]
+ elif subject != "":
+ if not line.startswith("Subject Public Key Info:"):
+ subject += line
+ else:
+ # Done, strip off quotes
+ subject = subject[1:-1]
+ break
+
+ # Issuer
+ issuer = ""
+ for line in certdetails.splitlines():
+ line = line.lstrip()
+ if line.startswith("Issuer: "):
+ issuer = line.split("Issuer: ")[1].split("\n")[0]
+ elif issuer != "":
+ if not line.startswith("Validity:"):
+ issuer += line
+ else:
+ issuer = issuer[1:-1]
+ break
+
+ return ([nickname, subject, issuer, str(end_date), trust_flags])
+
+ # Did not find cert with that name
+ raise ValueError("Certificate '{}' not found in NSS database".format(nickname))
+
+
+ def list_certs(self, ca=False):
+ all_certs = self._rsa_cert_list()
+ certs = []
+ for cert in all_certs:
+ trust_flags = cert[1]
+ if (ca and "CT" in trust_flags) or (not ca and "CT" not in trust_flags):
+ certs.append(self.get_cert_details(cert[0]))
+ return certs
+
+
+ def add_cert(self, nickname, input_file, ca=False):
+ """Add server or CA cert
+ """
+
+ # Verify input_file exists
+ if not os.path.exists(input_file):
+ raise ValueError("The certificate file ({}) does not exist".format(input_file))
+
+ if ca:
+ trust_flags = "CT,,"
+ else:
+ trust_flags = ",,"
+
+ cmd = [
+ '/usr/bin/certutil',
+ '-A',
+ '-d', self._certdb,
+ '-n', nickname,
+ '-t', trust_flags,
+ '-i', input_file,
+ '-a',
+ '-f',
+ '%s/%s' % (self._certdb, PWD_TXT),
+ ]
+ self.log.debug("add_cert cmd: %s", format_cmd_list(cmd))
+ check_output(cmd, stderr=subprocess.STDOUT)
diff --git a/src/lib389/lib389/utils.py b/src/lib389/lib389/utils.py
index 1472309..10c8cae 100644
--- a/src/lib389/lib389/utils.py
+++ b/src/lib389/lib389/utils.py
@@ -234,7 +234,7 @@ def selinux_label_port(port, remove_label=False):
"""
Either set or remove an SELinux label(ldap_port_t) for a TCP port
- :param port: The TCP port to be labelled
+ :param port: The TCP port to be labeled
:type port: str
:param remove_label: Set True if the port label should be removed
:type remove_label: boolean
@@ -258,9 +258,10 @@ def selinux_label_port(port, remove_label=False):
# We only label ports that ARE NOT in the default policy that comes with
# a RH based system.
+ port = int(port)
selinux_default_ports = [389, 636, 3268, 3269, 7389]
if port in selinux_default_ports:
- log.debug('port %s already in %s, skipping port relabel' % (port, selinux_default_ports))
+ log.debug('port {} already in {}, skipping port relabel'.format(port, selinux_default_ports))
return
label_set = False
@@ -283,11 +284,10 @@ def selinux_label_port(port, remove_label=False):
elif not remove_label:
# Port belongs to someone else (bad)
# This is only an issue during setting a label, not removing a label
- raise ValueError("Port {} was already labelled with: ({}) Please choose a different port number".format(port, policy['type']))
+ raise ValueError("Port {} was already labeled with: ({}) Please choose a different port number".format(port, policy['type']))
if (remove_label and label_set) or (not remove_label and not label_set):
for i in range(5):
-
try:
subprocess.check_call(["semanage", "port",
"-d" if remove_label else "-a",
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
4 years, 4 months