Skip to content

Commit 43323c9

Browse files
committed
Merge branch 'morpheus' into 'master'
* morpheus: MB-68871 Add --ipv6only and --ipv4only options to init-node MB-68657 Add CLI support for temporary passwords DOC-13099 Fix docs typo DOC-12245 Add `--ip` documentation to `reset-admin-password` Change-Id: I07d931c37cca5f7b4af4cd2013ca94a5353dfff5
2 parents f1cf991 + 89a229c commit 43323c9

8 files changed

Lines changed: 113 additions & 13 deletions

cbmgr.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,28 +2278,42 @@ def __init__(self):
22782278
help="Configure the node to communicate via ipv6")
22792279
group.add_argument("--ipv4", dest="ipv4", action="store_true", default=False,
22802280
help="Configure the node to communicate via ipv4")
2281+
group.add_argument("--ipv6only", dest="ipv6_only", action="store_true", default=False,
2282+
help="Configure the node to only use ipv6")
2283+
group.add_argument("--ipv4only", dest="ipv4_only", action="store_true", default=False,
2284+
help="Configure the node to only use ipv4")
22812285

22822286
@rest_initialiser(credentials_required=False)
22832287
def execute(self, opts):
22842288
# Cluster does not need to be initialized for this command
22852289

22862290
if (opts.data_path is None and opts.index_path is None and opts.analytics_path is None
22872291
and opts.eventing_path is None and opts.java_home is None and opts.hostname is None
2288-
and opts.ipv6 is None and opts.ipv4 is None):
2292+
and opts.ipv6 is None and opts.ipv4 is None and opts.ipv6_only is None and opts.ipv4_only is None):
22892293
_exit_if_errors(["No node initialization parameters specified"])
22902294

2291-
if opts.ipv4 and opts.ipv6:
2292-
_exit_if_errors(["Use either --ipv4 or --ipv6"])
2295+
flags_used = sum([opts.ipv4, opts.ipv6, opts.ipv6_only, opts.ipv4_only])
2296+
if flags_used > 1:
2297+
_exit_if_errors(["--ipv4, --ipv6, --ipv4only, and --ipv6only are mutually exclusive"])
2298+
2299+
afamily_only = None
22932300

22942301
if opts.ipv4:
22952302
afamily = 'ipv4'
22962303
elif opts.ipv6:
22972304
afamily = 'ipv6'
2305+
elif opts.ipv4_only:
2306+
afamily = 'ipv4'
2307+
afamily_only = True
2308+
elif opts.ipv6_only:
2309+
afamily = 'ipv6'
2310+
afamily_only = True
22982311
else:
22992312
afamily = None
23002313

23012314
_, errors = self.rest.node_init(hostname=opts.hostname,
23022315
afamily=afamily,
2316+
afamily_only=afamily_only,
23032317
data_path=opts.data_path,
23042318
index_path=opts.index_path,
23052319
cbas_path=opts.analytics_path,
@@ -4796,8 +4810,10 @@ def __init__(self):
47964810
group.add_argument("--group-name", dest="group", metavar="<group>", help="Group name")
47974811
group.add_argument("--group-description", dest="description", metavar="<text>", help="Group description")
47984812
group.add_argument("--ldap-ref", dest="ldap_ref", metavar="<ref>", help="LDAP group's distinguished name")
4813+
group.add_argument("--temporary-password", dest="temporary_password", action="store_true",
4814+
help="Force the user to change their password on next login (Enterprise Edition only)")
47994815

4800-
@rest_initialiser(cluster_init_check=True, version_check=True)
4816+
@rest_initialiser(cluster_init_check=True, version_check=True, enterprise_check=False)
48014817
def execute(self, opts):
48024818
num_selectors = sum([opts.delete, opts.list, opts.my_roles, opts.set, opts.get, opts.get_group,
48034819
opts.list_group, opts.delete_group, opts.set_group, opts.lock, opts.unlock])
@@ -4931,11 +4947,15 @@ def _set(self, opts):
49314947
if opts.rbac_pass is not None and opts.auth_domain == "external":
49324948
_warning("--rbac-password cannot be used with the external auth domain")
49334949
opts.rbac_pass = None
4950+
if opts.temporary_password and not self.enterprise:
4951+
_exit_if_errors(["--temporary-password is only supported on Enterprise Edition"])
4952+
if opts.temporary_password and opts.auth_domain == "external":
4953+
_exit_if_errors(["--temporary-password cannot be used for external users"])
49344954
if opts.auth_domain is None:
49354955
_exit_if_errors(["--auth-domain is required with the --set option"])
49364956

49374957
_, errors = self.rest.set_rbac_user(opts.rbac_user, opts.rbac_pass, opts.rbac_name, opts.roles,
4938-
opts.auth_domain, opts.groups)
4958+
opts.auth_domain, opts.groups, opts.temporary_password)
49394959
_exit_if_errors(errors)
49404960

49414961
if opts.roles is not None and "query_external_access" in opts.roles:

cluster_manager.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,7 @@ def get_bucket(self, name):
14271427

14281428
return None, ["Bucket not found"]
14291429

1430-
def node_init(self, hostname=None, afamily=None, data_path=None,
1430+
def node_init(self, hostname=None, afamily=None, afamily_only=None, data_path=None,
14311431
index_path=None, cbas_path=None, eventing_path=None,
14321432
java_home=None):
14331433
url = f'{self.hostname}/nodeInit'
@@ -1439,6 +1439,9 @@ def node_init(self, hostname=None, afamily=None, data_path=None,
14391439
if afamily is not None:
14401440
params["afamily"] = afamily
14411441

1442+
if afamily_only is not None:
1443+
params["afamilyOnly"] = "true" if afamily_only else "false"
1444+
14421445
if data_path is not None:
14431446
params["dataPath"] = data_path
14441447

@@ -1626,7 +1629,7 @@ def my_roles(self):
16261629
url = f'{self.hostname}/whoami'
16271630
return self._get(url)
16281631

1629-
def set_rbac_user(self, username, password, name, roles, auth_domain, groups):
1632+
def set_rbac_user(self, username, password, name, roles, auth_domain, groups, temporary_password=None):
16301633
if auth_domain is None:
16311634
return None, ["The authentication type is required"]
16321635

@@ -1656,6 +1659,8 @@ def set_rbac_user(self, username, password, name, roles, auth_domain, groups):
16561659
params['groups'] = groups
16571660
elif 'groups' in defaults:
16581661
params['groups'] = ','.join(defaults['groups'])
1662+
if temporary_password:
1663+
params['temporaryPassword'] = 'true'
16591664

16601665
return self._put(url, params)
16611666

docs/modules/cli/pages/cbcli/couchbase-cli-node-init.adoc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ _couchbase-cli node-init_ [--cluster <url>] [--username <user>] [--password <pas
1717
[--client-key-password <password>] [--node-init-data-path <path>]
1818
[--node-init-index-path <path>] [--node-init-analytics-path <path>]
1919
[--node-init-eventing-path <path>] [--node-init-hostname <hostname>]
20-
[--node-init-java-home <path>] [--ipv4] [--ipv6]
20+
[--node-init-java-home <path>] [--ipv4] [--ipv6] [--ipv4only] [--ipv6only]
2121

2222
== DESCRIPTION
2323

@@ -66,6 +66,14 @@ include::{partialsdir}/cbcli/part-common-options.adoc[]
6666
--ipv6::
6767
Switch the node to use ipv6 for node to node communication
6868

69+
--ipv4only::
70+
Switch the node to use ipv4 only for node to node communication. The node
71+
will only listen on that address.
72+
73+
--ipv6only::
74+
Switch the node to use ipv6 only for node to node communication. The node
75+
will only listen on that address.
76+
6977
include::{partialsdir}/cbcli/part-host-formats.adoc[]
7078

7179
include::{partialsdir}/cbcli/part-certificate-authentication.adoc[]

docs/modules/cli/pages/cbcli/couchbase-cli-node-to-node-encryption.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Changes node-to-node encryption
1212
== SYNOPSIS
1313

1414
[verse]
15-
_couchbase-cli node-to-node-encryption_ [--cluster <url>] [-username <username>] [--password <password>]
15+
_couchbase-cli node-to-node-encryption_ [--cluster <url>] [--username <username>] [--password <password>]
1616
[--client-cert <path>] [--client-cert-password <password>] [--client-key <path>]
1717
[--client-key-password <password>] [--get] [--enable] [--disable]
1818

docs/modules/cli/pages/cbcli/couchbase-cli-reset-admin-password.adoc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Resets the Couchbase Server administrator password
1313

1414
[verse]
1515
_couchbase-cli reset-admin-password_ [--regenerate] [--new-password <password>]
16-
[--port <port>] [--config-path <path>]
16+
[--port <port>] [--config-path <path>] [--ip]
1717

1818
== DESCRIPTION
1919

@@ -54,6 +54,11 @@ result, the command does not require credentials to be passed.
5454
Note: on Mac, this path is instead located in the user's home directory, at
5555
`~/Library/Application Support/Couchbase/var/lib/couchbase`.
5656

57+
-I::
58+
--ip::
59+
The IP address of the cluster, defaults to localhost. Useful if localhost
60+
is not set as the loopback address.
61+
5762
== EXAMPLES
5863

5964
To change the administrator password to `new_pwd`, run the following command:

docs/modules/cli/pages/cbcli/couchbase-cli-user-manage.adoc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ _couchbase-cli user-manage_ [--cluster <url>] [--username <user>] [--password <p
1919
[--set-group] [--delete-group] [--list-groups] [--get-group]
2020
[-- get] [--lock] [--unlock] [--rbac-username <username>]
2121
[--rbac-password <password>] [--rbac-name <name>] [--roles <roles_list>]
22-
[--auth-domain <domain>] [--user-groups <group>]
22+
[--auth-domain <domain>] [--user-groups <group>] [--temporary-password]
2323
[--group-description <text>] [--ldap-ref <ref>]
2424

2525
== DESCRIPTION
@@ -100,6 +100,10 @@ include::{partialsdir}/cbcli/part-common-options.adoc[]
100100
a user (--set) or when updating the users group, and
101101
should be specified as a comma separated list.
102102

103+
--temporary-password::
104+
Sets a temporary password which the user must change on their next login.
105+
This option can only be used with _local_ users.
106+
103107
--group-name <group>::
104108
Specifies the target group for the group operations (--set-group,
105109
--delete-group, --get-group).
@@ -456,6 +460,13 @@ $ couchbase-cli user-manage -c 127.0.0.1:8091 -u Administrator \
456460
--rbac-name "John Doe" --roles bucket_admin[default],replication_admin \
457461
--auth-domain local
458462
----
463+
To create a local user that with a temporary password (Enterprise Edition only):
464+
----
465+
$ couchbase-cli user-manage -c 127.0.0.1:8091 -u Administrator \
466+
-p password --set --rbac-username jdoe --rbac-password cbpass \
467+
--rbac-name "John Doe" --roles bucket_admin[default] \
468+
--auth-domain local --temporary-password
469+
----
459470
If you have external user source setup in your cluster and you want to add a
460471
user "John Doe" with username `jdoe` who should have the ability to manage only
461472
views for all bucket run the following command

jenkins/.aspell.en.pws

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ cbcli
2929
cbcollect
3030
cbdatarecovery
3131
cbexport
32-
cblogredaction
3332
cbimport
33+
cblogredaction
3434
CBO
3535
cbrecovery
3636
cbrestore

test/test_cli.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ def test_node_init(self):
12181218

12191219
def test_node_init_ipv6_and_ipv4(self):
12201220
self.system_exit_run(self.command + self.name_args + ['--ipv4', '--ipv6'], self.server_args)
1221-
self.assertIn('Use either --ipv4 or --ipv6', self.str_output)
1221+
self.assertIn('--ipv4, --ipv6, --ipv4only, and --ipv6only are mutually exclusive', self.str_output)
12221222

12231223
def test_node_init_ipv6(self):
12241224
self.no_error_run(self.command + self.name_args + ['--ipv6'], self.server_args)
@@ -1232,6 +1232,34 @@ def test_node_init_ipv4(self):
12321232
expected_params = ['hostname=foo', 'afamily=ipv4']
12331233
self.rest_parameter_match(expected_params)
12341234

1235+
def test_node_init_ipv6only(self):
1236+
self.no_error_run(self.command + self.name_args + ['--ipv6only'], self.server_args)
1237+
self.assertIn('POST:/nodeInit', self.server.trace)
1238+
expected_params = ['hostname=foo', 'afamily=ipv6', 'afamilyOnly=true']
1239+
self.rest_parameter_match(expected_params)
1240+
1241+
def test_node_init_ipv4only(self):
1242+
self.no_error_run(self.command + self.name_args + ['--ipv4only'], self.server_args)
1243+
self.assertIn('POST:/nodeInit', self.server.trace)
1244+
expected_params = ['hostname=foo', 'afamily=ipv4', 'afamilyOnly=true']
1245+
self.rest_parameter_match(expected_params)
1246+
1247+
def test_node_init_ipv6_and_ipv6only(self):
1248+
self.system_exit_run(self.command + self.name_args + ['--ipv6', '--ipv6only'], self.server_args)
1249+
self.assertIn('--ipv4, --ipv6, --ipv4only, and --ipv6only are mutually exclusive', self.str_output)
1250+
1251+
def test_node_init_ipv4_and_ipv6only(self):
1252+
self.system_exit_run(self.command + self.name_args + ['--ipv4', '--ipv6only'], self.server_args)
1253+
self.assertIn('--ipv4, --ipv6, --ipv4only, and --ipv6only are mutually exclusive', self.str_output)
1254+
1255+
def test_node_init_ipv4_and_ipv4only(self):
1256+
self.system_exit_run(self.command + self.name_args + ['--ipv4', '--ipv4only'], self.server_args)
1257+
self.assertIn('--ipv4, --ipv6, --ipv4only, and --ipv6only are mutually exclusive', self.str_output)
1258+
1259+
def test_node_init_ipv4only_and_ipv6only(self):
1260+
self.system_exit_run(self.command + self.name_args + ['--ipv4only', '--ipv6only'], self.server_args)
1261+
self.assertIn('--ipv4, --ipv6, --ipv4only, and --ipv6only are mutually exclusive', self.str_output)
1262+
12351263

12361264
class TestNodeReset(CommandTest):
12371265
def setUp(self):
@@ -2811,8 +2839,31 @@ def test_unlock(self):
28112839
self.assertIn('PATCH:/settings/rbac/users/local/username', self.server.trace)
28122840
expected_params = ['locked=false']
28132841
self.rest_parameter_match(expected_params)
2842+
2843+
def test_set_local_user_with_temporary_password(self):
2844+
self.no_error_run(self.command + ['--set', '--rbac-username', 'username', '--rbac-password', 'pwd',
2845+
'--auth-domain', 'local', '--roles', 'admin', '--rbac-name', 'name',
2846+
'--temporary-password'],
2847+
self.server_args)
2848+
self.assertIn('PUT:/settings/rbac/users/local/username', self.server.trace)
2849+
expected_params = ['name=name', 'password=pwd', 'roles=admin', 'temporaryPassword=true']
28142850
self.rest_parameter_match(expected_params)
28152851

2852+
def test_set_external_user_with_temporary_password(self):
2853+
self.system_exit_run(self.command + ['--set', '--rbac-username', 'username', '--auth-domain',
2854+
'external', '--roles', 'admin', '--rbac-name', 'name',
2855+
'--temporary-password'],
2856+
self.server_args)
2857+
self.assertIn('--temporary-password cannot be used for external users', self.str_output)
2858+
2859+
def test_set_local_user_with_temporary_password_community(self):
2860+
self.server_args['enterprise'] = False
2861+
self.system_exit_run(self.command + ['--set', '--rbac-username', 'username', '--rbac-password', 'pwd',
2862+
'--auth-domain', 'local', '--roles', 'admin', '--rbac-name', 'name',
2863+
'--temporary-password'],
2864+
self.server_args)
2865+
self.assertIn('--temporary-password is only supported on Enterprise Edition', self.str_output)
2866+
28162867

28172868
class TestMasterPassword(CommandTest):
28182869
def setUp(self):

0 commit comments

Comments
 (0)