• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

apache / iotdb / #10030

08 Sep 2023 01:34AM UTC coverage: 47.698%. First build
#10030

push

travis_ci

web-flow
[auth].fix cache info

37 of 37 new or added lines in 8 files covered. (100.0%)

80772 of 169340 relevant lines covered (47.7%)

0.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.18
/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
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
package org.apache.iotdb.commons.auth.user;
20

21
import org.apache.iotdb.commons.auth.entity.PathPrivilege;
22
import org.apache.iotdb.commons.auth.entity.User;
23
import org.apache.iotdb.commons.conf.IoTDBConstant;
24
import org.apache.iotdb.commons.file.SystemFileFactory;
25
import org.apache.iotdb.commons.utils.FileUtils;
26
import org.apache.iotdb.commons.utils.IOUtils;
27

28
import org.apache.thrift.TException;
29
import org.slf4j.Logger;
30
import org.slf4j.LoggerFactory;
31

32
import java.io.BufferedInputStream;
33
import java.io.BufferedOutputStream;
34
import java.io.DataInputStream;
35
import java.io.File;
36
import java.io.FileInputStream;
37
import java.io.IOException;
38
import java.nio.ByteBuffer;
39
import java.nio.file.Files;
40
import java.util.ArrayList;
41
import java.util.HashSet;
42
import java.util.List;
43
import java.util.Set;
44
import java.util.UUID;
45

46
/**
47
 * This class loads a user's information from the corresponding file.The user file is a sequential
48
 * file. User file schema: Int32 username bytes size Utf-8 username bytes Int32 Password bytes size
49
 * Utf-8 password bytes Int32 seriesPath privilege number n Int32 seriesPath[1] size Utf-8
50
 * seriesPath[1] bytes Int32 privilege num k1 Int32 privilege[1][1] Int32 privilege[1][2] ... Int32
51
 * privilege[1][k1] Int32 seriesPath[2] size Utf-8 seriesPath[2] bytes Int32 privilege num k2 Int32
52
 * privilege[2][1] Int32 privilege[2][2] ... Int32 privilege[2][k2] ... Int32 seriesPath[n] size
53
 * Utf-8 seriesPath[n] bytes Int32 privilege num kn Int32 privilege[n][1] Int32 privilege[n][2] ...
54
 * Int32 privilege[n][kn] Int32 user name number m Int32 user name[1] size Utf-8 user name[1] bytes
55
 * Int32 user name[2] size Utf-8 user name[2] bytes ... Int32 user name[m] size Utf-8 user name[m]
56
 * bytes
57
 */
58
public class LocalFileUserAccessor implements IUserAccessor {
59
  private static final Logger logger = LoggerFactory.getLogger(LocalFileUserAccessor.class);
1✔
60
  private static final String TEMP_SUFFIX = ".temp";
61
  private static final String STRING_ENCODING = "utf-8";
62
  private static final String userSnapshotFileName = "system" + File.separator + "users";
1✔
63

64
  private static final String ROLE_SUFFIX = "_role";
65

66
  private final String userDirPath;
67
  /**
68
   * Reused buffer for primitive types encoding/decoding, which aim to reduce memory fragments. Use
69
   * ThreadLocal for thread safety.
70
   */
71
  private final ThreadLocal<ByteBuffer> encodingBufferLocal = new ThreadLocal<>();
1✔
72

73
  private final ThreadLocal<byte[]> strBufferLocal = new ThreadLocal<>();
1✔
74

75
  public LocalFileUserAccessor(String userDirPath) {
1✔
76
    this.userDirPath = userDirPath;
1✔
77
  }
1✔
78

79
  /**
80
   * Deserialize a user from its user file.
81
   *
82
   * @param username The name of the user to be deserialized.
83
   * @return The user object or null if no such user.
84
   */
85
  @Override
86
  public User loadUser(String username) throws IOException {
87
    File userProfile =
1✔
88
        SystemFileFactory.INSTANCE.getFile(
1✔
89
            userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX);
90
    if (!userProfile.exists() || !userProfile.isFile()) {
1✔
91
      // System may crush before a newer file is renamed.
92
      File newProfile =
1✔
93
          SystemFileFactory.INSTANCE.getFile(
1✔
94
              userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX);
95
      if (newProfile.exists() && newProfile.isFile()) {
1✔
96
        if (!newProfile.renameTo(userProfile)) {
×
97
          logger.error("New profile renaming not succeed.");
×
98
        }
99
        userProfile = newProfile;
×
100
      } else {
101
        return null;
1✔
102
      }
103
    }
104

105
    FileInputStream inputStream = new FileInputStream(userProfile);
1✔
106
    try (DataInputStream dataInputStream =
1✔
107
        new DataInputStream(new BufferedInputStream(inputStream))) {
108
      User user = new User();
1✔
109
      user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal));
1✔
110
      user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal));
1✔
111
      user.setSysPrivilegeSet(dataInputStream.readInt());
1✔
112
      List<PathPrivilege> pathPrivilegeList = new ArrayList<>();
1✔
113
      for (int i = 0; dataInputStream.available() != 0; i++) {
1✔
114
        pathPrivilegeList.add(
1✔
115
            IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal));
1✔
116
      }
117
      user.setPrivilegeList(pathPrivilegeList);
1✔
118

119
      File roleOfUser =
1✔
120
          SystemFileFactory.INSTANCE.getFile(
1✔
121
              userDirPath, File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX);
122

123
      if (!roleOfUser.isFile() || !roleOfUser.exists()) {
1✔
124
        // System may crush before a newer file is renamed.
125
        File newRoleProfile =
×
126
            SystemFileFactory.INSTANCE.getFile(
×
127
                userDirPath
128
                    + File.separator
129
                    + username
130
                    + "_role"
131
                    + IoTDBConstant.PROFILE_SUFFIX
132
                    + TEMP_SUFFIX);
133
        if (newRoleProfile.exists() && newRoleProfile.isFile()) {
×
134
          if (!newRoleProfile.renameTo(roleOfUser)) {
×
135
            logger.warn(" New role profile renaming not succeed.");
×
136
          }
137
        }
138
      }
139

140
      roleOfUser =
1✔
141
          SystemFileFactory.INSTANCE.getFile(
1✔
142
              userDirPath, File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX);
143

144
      if (roleOfUser.exists()) {
1✔
145
        inputStream = new FileInputStream(roleOfUser);
1✔
146
        try (DataInputStream roleInpuStream =
1✔
147
            new DataInputStream(new BufferedInputStream(inputStream))) {
148
          List<String> roleList = new ArrayList<>();
1✔
149
          for (int i = 0; roleInpuStream.available() != 0; i++) {
1✔
150
            String rolename = IOUtils.readString(roleInpuStream, STRING_ENCODING, strBufferLocal);
1✔
151
            roleList.add(rolename);
1✔
152
          }
153
          user.setRoleList(roleList);
1✔
154
        }
155
      }
156

157
      //      // for online upgrading, profile for v0.9.x/v1 does not contain waterMark
158
      //      long userProfileLength = userProfile.length();
159
      //      try {
160
      //        user.setUseWaterMark(dataInputStream.readInt() != 0);
161
      //      } catch (EOFException e1) {
162
      //        user.setUseWaterMark(false);
163
      //        try (RandomAccessFile file = new RandomAccessFile(userProfile, "rw")) {
164
      //          file.seek(userProfileLength);
165
      //          file.writeInt(0);
166
      //        }
167
      //      }
168
      return user;
1✔
169
    } catch (Exception e) {
×
170
      throw new IOException(e);
×
171
    } finally {
172
      strBufferLocal.remove();
1✔
173
    }
174
  }
175

176
  /**
177
   * Serialize the user object to a temp file, then replace the old user file with the new file.
178
   *
179
   * @param user The user object that is to be saved.
180
   */
181
  @Override
182
  public void saveUser(User user) throws IOException {
183
    File userProfile =
1✔
184
        SystemFileFactory.INSTANCE.getFile(
1✔
185
            userDirPath
186
                + File.separator
187
                + user.getName()
1✔
188
                + IoTDBConstant.PROFILE_SUFFIX
189
                + TEMP_SUFFIX);
190

191
    try (BufferedOutputStream outputStream =
1✔
192
        new BufferedOutputStream(Files.newOutputStream(userProfile.toPath()))) {
1✔
193
      try {
194
        IOUtils.writeString(outputStream, user.getName(), STRING_ENCODING, encodingBufferLocal);
1✔
195
        IOUtils.writeString(outputStream, user.getPassword(), STRING_ENCODING, encodingBufferLocal);
1✔
196
        IOUtils.writeInt(outputStream, user.getAllSysPrivileges(), encodingBufferLocal);
1✔
197

198
        int privilegeNum = user.getPathPrivilegeList().size();
1✔
199
        for (int i = 0; i < privilegeNum; i++) {
1✔
200
          PathPrivilege pathPrivilege = user.getPathPrivilegeList().get(i);
1✔
201
          IOUtils.writePathPrivilege(
1✔
202
              outputStream, pathPrivilege, STRING_ENCODING, encodingBufferLocal);
203
        }
204

205
        outputStream.flush();
1✔
206
      } catch (Exception e) {
×
207
        throw new IOException(e);
×
208
      }
1✔
209
    } finally {
210
      encodingBufferLocal.remove();
1✔
211
    }
212

213
    File roleProfile =
1✔
214
        SystemFileFactory.INSTANCE.getFile(
1✔
215
            userDirPath
216
                + File.separator
217
                + user.getName()
1✔
218
                + ROLE_SUFFIX
219
                + IoTDBConstant.PROFILE_SUFFIX
220
                + TEMP_SUFFIX);
221
    try (BufferedOutputStream roleOutputStream =
1✔
222
        new BufferedOutputStream(Files.newOutputStream(roleProfile.toPath()))) {
1✔
223
      try {
224
        int userNum = user.getRoleList().size();
1✔
225
        for (int i = 0; i < userNum; i++) {
1✔
226
          IOUtils.writeString(
1✔
227
              roleOutputStream, user.getRoleList().get(i), STRING_ENCODING, encodingBufferLocal);
1✔
228
        }
229
        roleOutputStream.flush();
1✔
230
      } catch (Exception e) {
×
231
        throw new IOException(e);
×
232
      }
1✔
233
    } finally {
234
      encodingBufferLocal.remove();
1✔
235
    }
236

237
    File oldUserFile =
1✔
238
        SystemFileFactory.INSTANCE.getFile(
1✔
239
            userDirPath + File.separator + user.getName() + IoTDBConstant.PROFILE_SUFFIX);
1✔
240
    IOUtils.replaceFile(userProfile, oldUserFile);
1✔
241
    File oldURoleFile =
1✔
242
        SystemFileFactory.INSTANCE.getFile(
1✔
243
            userDirPath
244
                + File.separator
245
                + user.getName()
1✔
246
                + ROLE_SUFFIX
247
                + IoTDBConstant.PROFILE_SUFFIX);
248
    IOUtils.replaceFile(roleProfile, oldURoleFile);
1✔
249
  }
1✔
250

251
  /**
252
   * Delete a user's user file.
253
   *
254
   * @param username The name of the user to be deleted.
255
   * @return True if the file is successfully deleted, false if the file does not exists.
256
   * @throws IOException when the file cannot be deleted.
257
   */
258
  @Override
259
  public boolean deleteUser(String username) throws IOException {
260
    File userProfile =
1✔
261
        SystemFileFactory.INSTANCE.getFile(
1✔
262
            userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX);
263
    File backFile =
1✔
264
        SystemFileFactory.INSTANCE.getFile(
1✔
265
            userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX);
266
    // The user may be not flush. So if not find the file, just return true;
267
    if (!userProfile.exists() && !backFile.exists()) {
1✔
268
      return true;
1✔
269
    }
270
    if ((userProfile.exists() && !userProfile.delete())
1✔
271
        || (backFile.exists() && !backFile.delete())) {
1✔
272
      throw new IOException(String.format("Cannot delete user file of %s", username));
×
273
    }
274
    if (!deleteURole(username)) {
1✔
275
      return false;
×
276
    }
277
    return true;
1✔
278
  }
279

280
  public boolean deleteURole(String username) throws IOException {
281
    File uRoleProfile =
1✔
282
        SystemFileFactory.INSTANCE.getFile(
1✔
283
            userDirPath + File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX);
284
    File backProfile =
1✔
285
        SystemFileFactory.INSTANCE.getFile(
1✔
286
            userDirPath
287
                + File.separator
288
                + username
289
                + ROLE_SUFFIX
290
                + IoTDBConstant.PROFILE_SUFFIX
291
                + TEMP_SUFFIX);
292
    // This role don't have any role.
293
    if (!uRoleProfile.exists() && !backProfile.exists()) {
1✔
294
      return true;
×
295
    }
296
    if ((uRoleProfile.exists() && !uRoleProfile.delete())
1✔
297
        || (backProfile.exists() && !backProfile.delete())) {
1✔
298
      throw new IOException(String.format("Catch error when delete %s 's role", username));
×
299
    }
300
    return true;
1✔
301
  }
302

303
  @Override
304
  public List<String> listAllUsers() {
305
    File userDir = SystemFileFactory.INSTANCE.getFile(userDirPath);
1✔
306
    String[] names =
1✔
307
        userDir.list(
1✔
308
            (dir, name) ->
309
                (name.endsWith(IoTDBConstant.PROFILE_SUFFIX)
1✔
310
                        && !name.endsWith("_role" + IoTDBConstant.PROFILE_SUFFIX))
1✔
311
                    || (name.endsWith(TEMP_SUFFIX) && !name.endsWith("_role" + TEMP_SUFFIX)));
1✔
312
    List<String> retList = new ArrayList<>();
1✔
313
    if (names != null) {
1✔
314
      // in very rare situations, normal file and backup file may exist at the same time
315
      // so a set is used to deduplicate
316
      Set<String> set = new HashSet<>();
1✔
317
      for (String fileName : names) {
1✔
318
        set.add(fileName.replace(IoTDBConstant.PROFILE_SUFFIX, "").replace(TEMP_SUFFIX, ""));
1✔
319
      }
320
      retList.addAll(set);
1✔
321
    }
322
    return retList;
1✔
323
  }
324

325
  @Override
326
  public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException {
327
    SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE;
1✔
328
    File userFolder = systemFileFactory.getFile(userDirPath);
1✔
329
    File userSnapshotDir = systemFileFactory.getFile(snapshotDir, userSnapshotFileName);
1✔
330
    File userTmpSnapshotDir =
1✔
331
        systemFileFactory.getFile(userSnapshotDir.getAbsolutePath() + "-" + UUID.randomUUID());
1✔
332

333
    boolean result = true;
1✔
334
    try {
335
      result = FileUtils.copyDir(userFolder, userTmpSnapshotDir);
1✔
336
      result &= userTmpSnapshotDir.renameTo(userSnapshotDir);
1✔
337
    } finally {
338
      if (userTmpSnapshotDir.exists() && !userTmpSnapshotDir.delete()) {
1✔
339
        FileUtils.deleteDirectory(userTmpSnapshotDir);
×
340
      }
341
    }
342
    return result;
1✔
343
  }
344

345
  @Override
346
  public void processLoadSnapshot(File snapshotDir) throws TException, IOException {
347
    SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE;
1✔
348
    File userFolder = systemFileFactory.getFile(userDirPath);
1✔
349
    File userTmpFolder =
1✔
350
        systemFileFactory.getFile(userFolder.getAbsolutePath() + "-" + UUID.randomUUID());
1✔
351
    File userSnapshotDir = systemFileFactory.getFile(snapshotDir, userSnapshotFileName);
1✔
352

353
    try {
354
      org.apache.commons.io.FileUtils.moveDirectory(userFolder, userTmpFolder);
1✔
355
      if (!FileUtils.copyDir(userSnapshotDir, userFolder)) {
1✔
356
        logger.error("Failed to load user folder snapshot and rollback.");
×
357
        // rollback if failed to copy
358
        FileUtils.deleteDirectory(userFolder);
×
359
        org.apache.commons.io.FileUtils.moveDirectory(userTmpFolder, userFolder);
×
360
      }
361
    } finally {
362
      FileUtils.deleteDirectory(userTmpFolder);
1✔
363
    }
364
  }
1✔
365

366
  @Override
367
  public void reset() {
368
    if (SystemFileFactory.INSTANCE.getFile(userDirPath).mkdirs()) {
1✔
369
      logger.info("user info dir {} is created", userDirPath);
1✔
370
    } else if (!SystemFileFactory.INSTANCE.getFile(userDirPath).exists()) {
1✔
371
      logger.error("user info dir {} can not be created", userDirPath);
×
372
    }
373
  }
1✔
374

375
  @Override
376
  public String getDirPath() {
377
    return userDirPath;
×
378
  }
379

380
  @Override
381
  public void cleanUserFolder() {
382
    File[] files = SystemFileFactory.INSTANCE.getFile(userDirPath).listFiles();
1✔
383
    for (File file : files) {
1✔
384
      FileUtils.deleteFileIfExist(file);
×
385
    }
386
  }
1✔
387
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc