Skip to content

Commit 540c008

Browse files
CaideyipiJackieTien97
authored andcommitted
Fix account unlock propagation for pipe password checks (#17814)
* Fix account unlock propagation for pipe password checks * Fix account unlock user validation
1 parent 9a2323e commit 540c008

12 files changed

Lines changed: 201 additions & 25 deletions

File tree

integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ public void testIllegalPassword() throws Exception {
595595

596596
try {
597597
statement.execute("alter pipe a2b modify source ('password'='fake')");
598+
fail();
598599
} catch (final SQLException e) {
599600
Assert.assertEquals("801: Failed to check password for pipe a2b.", e.getMessage());
600601
}

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ public static ConfigPhysicalPlan create(final ByteBuffer buffer) throws IOExcept
311311
case UpdateUserV2:
312312
case CreateUserWithRawPassword:
313313
case RenameUser:
314+
case AccountUnlock:
314315
plan = new AuthorTreePlan(configPhysicalPlanType);
315316
break;
316317
case RCreateUser:
@@ -343,6 +344,7 @@ public static ConfigPhysicalPlan create(final ByteBuffer buffer) throws IOExcept
343344
case RRevokeUserSysPri:
344345
case RRevokeRoleSysPri:
345346
case RRenameUser:
347+
case RAccountUnlock:
346348
plan = new AuthorRelationalPlan(configPhysicalPlanType);
347349
break;
348350
case ApplyConfigNode:

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ public enum ConfigPhysicalPlanType {
333333
RDropUserV2((short) 2103),
334334
RenameUser((short) 2104),
335335
RRenameUser((short) 2105),
336+
AccountUnlock((short) 2106),
336337

337338
EnableSeparationOfAdminPowers((short) 2200),
338339

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanVisitor.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ public R process(final ConfigPhysicalPlan plan, final C context) {
104104
return visitGrantRoleToUser((AuthorTreePlan) plan, context);
105105
case RevokeRoleFromUser:
106106
return visitRevokeRoleFromUser((AuthorTreePlan) plan, context);
107+
case AccountUnlock:
108+
return visitAccountUnlock((AuthorTreePlan) plan, context);
107109
case RCreateUser:
108110
return visitRCreateUser((AuthorRelationalPlan) plan, context);
109111
case RCreateRole:
@@ -160,6 +162,8 @@ public R process(final ConfigPhysicalPlan plan, final C context) {
160162
return visitRRevokeUserSysPrivilege((AuthorRelationalPlan) plan, context);
161163
case RRevokeRoleSysPri:
162164
return visitRRevokeRoleSysPrivilege((AuthorRelationalPlan) plan, context);
165+
case RAccountUnlock:
166+
return visitRAccountUnlock((AuthorRelationalPlan) plan, context);
163167
case SetTTL:
164168
return visitTTL((SetTTLPlan) plan, context);
165169
case PipeCreateTableOrView:
@@ -310,6 +314,10 @@ public R visitRevokeRoleFromUser(final AuthorTreePlan revokeRoleFromUserPlan, fi
310314
return visitPlan(revokeRoleFromUserPlan, context);
311315
}
312316

317+
public R visitAccountUnlock(final AuthorTreePlan accountUnlockPlan, final C context) {
318+
return visitPlan(accountUnlockPlan, context);
319+
}
320+
313321
public R visitRCreateUser(final AuthorRelationalPlan rCreateUserPlan, final C context) {
314322
return visitPlan(rCreateUserPlan, context);
315323
}
@@ -426,6 +434,10 @@ public R visitRRevokeRoleSysPrivilege(
426434
return visitPlan(rRevokeRoleSysPrivilegePlan, context);
427435
}
428436

437+
public R visitRAccountUnlock(final AuthorRelationalPlan rAccountUnlockPlan, final C context) {
438+
return visitPlan(rAccountUnlockPlan, context);
439+
}
440+
429441
public R visitTTL(final SetTTLPlan setTTLPlan, final C context) {
430442
return visitPlan(setTTLPlan, context);
431443
}

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/auth/AuthorInfo.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ public static ConfigPhysicalPlanType getConfigPhysicalPlanTypeFromAuthorType(int
7474
throw new IndexOutOfBoundsException(ConfigNodeMessages.INVALID_AUTHOR_TYPE_ORDINAL);
7575
}
7676
ConfigPhysicalPlanType configPhysicalPlanType;
77-
if (authorType >= AuthorType.RENAME_USER.ordinal()) {
77+
if (authorType == AuthorType.ACCOUNT_UNLOCK.ordinal()) {
78+
return ConfigPhysicalPlanType.AccountUnlock;
79+
} else if (authorType >= AuthorType.RENAME_USER.ordinal()) {
7880
AuthorType type = AuthorType.values()[authorType];
7981
switch (type) {
8082
case RENAME_USER:
@@ -105,6 +107,8 @@ public static ConfigPhysicalPlanType getConfigPhysicalPlanTypeFromAuthorRType(in
105107
ConfigPhysicalPlanType configPhysicalPlanType;
106108
if (authorRType == AuthorRType.RENAME_USER.ordinal()) {
107109
configPhysicalPlanType = ConfigPhysicalPlanType.RRenameUser;
110+
} else if (authorRType == AuthorRType.ACCOUNT_UNLOCK.ordinal()) {
111+
configPhysicalPlanType = ConfigPhysicalPlanType.RAccountUnlock;
108112
} else {
109113
configPhysicalPlanType =
110114
ConfigPhysicalPlanType.values()[

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/auth/AuthorPlanExecutor.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ public TSStatus executeAuthorNonQuery(AuthorTreePlan authorPlan) {
124124
case RenameUser:
125125
authorizer.renameUser(userName, newUsername);
126126
break;
127+
case AccountUnlock:
128+
checkUserExistsForAccountUnlock(userName);
129+
break;
127130
case CreateUser:
128131
authorizer.createUser(userName, password);
129132
break;
@@ -241,6 +244,7 @@ public TSStatus executeRelationalAuthorNonQuery(AuthorRelationalPlan authorPlan)
241244
authorizer.renameUser(userName, newUsername);
242245
break;
243246
case RAccountUnlock:
247+
checkUserExistsForAccountUnlock(userName);
244248
break;
245249
case RDropRole:
246250
authorizer.deleteRole(roleName);
@@ -448,6 +452,14 @@ public TSStatus executeRelationalAuthorNonQuery(AuthorRelationalPlan authorPlan)
448452
return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS);
449453
}
450454

455+
private void checkUserExistsForAccountUnlock(final String userName) throws AuthException {
456+
// Account unlock has no persistent ConfigNode auth state change, but the write path needs this
457+
// validation before broadcasting DataNode unlocks and propagating through pipe.
458+
if (authorizer.getUser(userName) == null) {
459+
throw new AuthException(TSStatusCode.USER_NOT_EXIST, NO_USER_MSG + userName);
460+
}
461+
}
462+
451463
@Override
452464
public PermissionInfoResp executeListUsers(final AuthorPlan plan) throws AuthException {
453465
final PermissionInfoResp result = new PermissionInfoResp();

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ public TSStatus executeNonQueryPlan(ConfigPhysicalPlan physicalPlan)
488488
case RevokeRoleFromUserDep:
489489
case UpdateUserDep:
490490
case RenameUser:
491+
case AccountUnlock:
491492
case RCreateRole:
492493
case RCreateUser:
493494
case RDropUser:

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedure.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.iotdb.confignode.client.sync.CnToDnSyncRequestType;
2929
import org.apache.iotdb.confignode.client.sync.SyncDataNodeClientPool;
3030
import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan;
31+
import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
3132
import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan;
3233
import org.apache.iotdb.confignode.consensus.request.write.pipe.payload.PipeEnrichedPlan;
3334
import org.apache.iotdb.confignode.i18n.ProcedureMessages;
@@ -100,6 +101,11 @@ protected Flow executeFromState(ConfigNodeProcedureEnv env, AuthOperationProcedu
100101
TSStatus status;
101102
req.setUsername(user);
102103
req.setRoleName(role);
104+
if (plan.getAuthorType() == ConfigPhysicalPlanType.AccountUnlock
105+
|| plan.getAuthorType() == ConfigPhysicalPlanType.RAccountUnlock) {
106+
// For account unlock, role carries the optional login address.
107+
req.setNeedDisconnect(true);
108+
}
103109
Iterator<Pair<TDataNodeConfiguration, Long>> it = dataNodesToInvalid.iterator();
104110
while (it.hasNext()) {
105111
Pair<TDataNodeConfiguration, Long> pair = it.next();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.confignode.persistence.auth;
21+
22+
import org.apache.iotdb.common.rpc.thrift.TSStatus;
23+
import org.apache.iotdb.commons.auth.authorizer.IAuthorizer;
24+
import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
25+
import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan;
26+
import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan;
27+
import org.apache.iotdb.rpc.TSStatusCode;
28+
29+
import org.junit.Assert;
30+
import org.junit.Test;
31+
32+
import java.util.Collections;
33+
34+
import static org.mockito.Mockito.mock;
35+
import static org.mockito.Mockito.when;
36+
37+
public class AuthorPlanExecutorTest {
38+
39+
@Test
40+
public void testAccountUnlockRequiresExistingUser() throws Exception {
41+
final IAuthorizer authorizer = mock(IAuthorizer.class);
42+
when(authorizer.getUser("missing")).thenReturn(null);
43+
44+
final AuthorPlanExecutor executor = new AuthorPlanExecutor(authorizer);
45+
final TSStatus status =
46+
executor.executeAuthorNonQuery(
47+
new AuthorTreePlan(
48+
ConfigPhysicalPlanType.AccountUnlock,
49+
"missing",
50+
"",
51+
"",
52+
"",
53+
Collections.emptySet(),
54+
false,
55+
Collections.emptyList()));
56+
57+
Assert.assertEquals(TSStatusCode.USER_NOT_EXIST.getStatusCode(), status.getCode());
58+
}
59+
60+
@Test
61+
public void testRAccountUnlockRequiresExistingUser() throws Exception {
62+
final IAuthorizer authorizer = mock(IAuthorizer.class);
63+
when(authorizer.getUser("missing")).thenReturn(null);
64+
65+
final AuthorPlanExecutor executor = new AuthorPlanExecutor(authorizer);
66+
final TSStatus status =
67+
executor.executeRelationalAuthorNonQuery(
68+
new AuthorRelationalPlan(
69+
ConfigPhysicalPlanType.RAccountUnlock,
70+
"missing",
71+
"",
72+
"",
73+
"",
74+
Collections.emptySet(),
75+
false,
76+
""));
77+
78+
Assert.assertEquals(TSStatusCode.USER_NOT_EXIST.getStatusCode(), status.getCode());
79+
}
80+
}

iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedureTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,34 @@ public void serializeDeserializeTest() throws IOException {
9898
fail();
9999
}
100100

101+
try {
102+
final AuthOperationProcedure proc =
103+
new AuthOperationProcedure(
104+
new AuthorTreePlan(
105+
ConfigPhysicalPlanType.AccountUnlock,
106+
"user1",
107+
"",
108+
"",
109+
"",
110+
Collections.emptySet(),
111+
false,
112+
Collections.emptyList()),
113+
datanodes,
114+
false);
115+
proc.serialize(outputStream);
116+
final ByteBuffer buffer =
117+
ByteBuffer.wrap(byteArrayOutputStream.getBuf(), 0, byteArrayOutputStream.size());
118+
119+
final AuthOperationProcedure proc2 =
120+
(AuthOperationProcedure) ProcedureFactory.getInstance().create(buffer);
121+
Assert.assertEquals(proc, proc2);
122+
buffer.clear();
123+
byteArrayOutputStream.reset();
124+
} catch (final Exception e) {
125+
e.printStackTrace();
126+
fail();
127+
}
128+
101129
try {
102130
final int begin = ConfigPhysicalPlanType.RCreateUser.ordinal();
103131
final int end = ConfigPhysicalPlanType.RRevokeRoleSysPri.ordinal();
@@ -129,5 +157,33 @@ public void serializeDeserializeTest() throws IOException {
129157
e.printStackTrace();
130158
fail();
131159
}
160+
161+
try {
162+
final AuthOperationProcedure proc =
163+
new AuthOperationProcedure(
164+
new AuthorRelationalPlan(
165+
ConfigPhysicalPlanType.RAccountUnlock,
166+
"user1",
167+
"127.0.0.1",
168+
"",
169+
"",
170+
Collections.emptySet(),
171+
false,
172+
""),
173+
datanodes,
174+
false);
175+
proc.serialize(outputStream);
176+
final ByteBuffer buffer =
177+
ByteBuffer.wrap(byteArrayOutputStream.getBuf(), 0, byteArrayOutputStream.size());
178+
179+
final AuthOperationProcedure proc2 =
180+
(AuthOperationProcedure) ProcedureFactory.getInstance().create(buffer);
181+
Assert.assertEquals(proc, proc2);
182+
buffer.clear();
183+
byteArrayOutputStream.reset();
184+
} catch (final Exception e) {
185+
e.printStackTrace();
186+
fail();
187+
}
132188
}
133189
}

0 commit comments

Comments
 (0)