mirror of https://github.com/apache/jclouds.git
Issue-1020 Add full name option for AdminUser and UserAdd
This commit is contained in:
parent
6016272486
commit
7227d70c4e
|
@ -109,6 +109,7 @@ public class AdminAccess implements Statement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String adminUsername;
|
private String adminUsername;
|
||||||
|
private String adminFullName;
|
||||||
private String adminHome;
|
private String adminHome;
|
||||||
private String adminPublicKey;
|
private String adminPublicKey;
|
||||||
private File adminPublicKeyFile;
|
private File adminPublicKeyFile;
|
||||||
|
@ -127,6 +128,12 @@ public class AdminAccess implements Statement {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AdminAccess.Builder adminFullName(String adminFullName) {
|
||||||
|
this.adminFullName = adminFullName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public AdminAccess.Builder adminHome(String adminHome) {
|
public AdminAccess.Builder adminHome(String adminHome) {
|
||||||
this.adminHome = adminHome;
|
this.adminHome = adminHome;
|
||||||
return this;
|
return this;
|
||||||
|
@ -211,7 +218,7 @@ public class AdminAccess implements Statement {
|
||||||
String adminPrivateKey = this.adminPrivateKey;
|
String adminPrivateKey = this.adminPrivateKey;
|
||||||
if (adminPrivateKey == null && adminPrivateKeyFile != null)
|
if (adminPrivateKey == null && adminPrivateKeyFile != null)
|
||||||
adminPrivateKey = Files.toString(adminPrivateKeyFile, UTF_8);
|
adminPrivateKey = Files.toString(adminPrivateKeyFile, UTF_8);
|
||||||
return new Config(adminUsername, adminHome, adminPublicKey, adminPrivateKey, adminPassword, loginPassword,
|
return new Config(adminUsername, adminFullName, adminHome, adminPublicKey, adminPrivateKey, adminPassword, loginPassword,
|
||||||
lockSsh, grantSudoToAdminUser, authorizeAdminPublicKey, installAdminPrivateKey, resetLoginPassword,
|
lockSsh, grantSudoToAdminUser, authorizeAdminPublicKey, installAdminPrivateKey, resetLoginPassword,
|
||||||
cryptFunction);
|
cryptFunction);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -222,6 +229,7 @@ public class AdminAccess implements Statement {
|
||||||
|
|
||||||
protected static class Config {
|
protected static class Config {
|
||||||
private final String adminUsername;
|
private final String adminUsername;
|
||||||
|
private final String adminFullName;
|
||||||
private final String adminHome;
|
private final String adminHome;
|
||||||
private final String adminPublicKey;
|
private final String adminPublicKey;
|
||||||
private final String adminPrivateKey;
|
private final String adminPrivateKey;
|
||||||
|
@ -235,11 +243,12 @@ public class AdminAccess implements Statement {
|
||||||
private boolean authorizeAdminPublicKey;
|
private boolean authorizeAdminPublicKey;
|
||||||
private boolean lockSsh;
|
private boolean lockSsh;
|
||||||
|
|
||||||
protected Config(@Nullable String adminUsername, @Nullable String adminHome, @Nullable String adminPublicKey,
|
protected Config(@Nullable String adminUsername, @Nullable String adminFullName, @Nullable String adminHome, @Nullable String adminPublicKey,
|
||||||
@Nullable String adminPrivateKey, @Nullable String adminPassword, @Nullable String loginPassword,
|
@Nullable String adminPrivateKey, @Nullable String adminPassword, @Nullable String loginPassword,
|
||||||
boolean lockSsh, boolean grantSudoToAdminUser, boolean authorizeAdminPublicKey,
|
boolean lockSsh, boolean grantSudoToAdminUser, boolean authorizeAdminPublicKey,
|
||||||
boolean installAdminPrivateKey, boolean resetLoginPassword, Function<String, String> cryptFunction) {
|
boolean installAdminPrivateKey, boolean resetLoginPassword, Function<String, String> cryptFunction) {
|
||||||
this.adminUsername = adminUsername;
|
this.adminUsername = adminUsername;
|
||||||
|
this.adminFullName = adminFullName;
|
||||||
this.adminHome = adminHome;
|
this.adminHome = adminHome;
|
||||||
this.adminPublicKey = adminPublicKey;
|
this.adminPublicKey = adminPublicKey;
|
||||||
this.adminPrivateKey = adminPrivateKey;
|
this.adminPrivateKey = adminPrivateKey;
|
||||||
|
@ -267,6 +276,10 @@ public class AdminAccess implements Statement {
|
||||||
return adminUsername;
|
return adminUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAdminFullName() {
|
||||||
|
return adminFullName;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAdminHome() {
|
public String getAdminHome() {
|
||||||
return adminHome;
|
return adminHome;
|
||||||
}
|
}
|
||||||
|
@ -318,7 +331,8 @@ public class AdminAccess implements Statement {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.append("Config [adminUsername=").append(adminUsername).append(", adminHome=").append(adminHome)
|
builder.append("Config [adminUsername=").append(adminUsername).append(", adminFullName=").append(adminFullName)
|
||||||
|
.append(", adminHome=").append(adminHome)
|
||||||
.append(", adminPublicKey=").append(adminPublicKey == null ? "null" : "present")
|
.append(", adminPublicKey=").append(adminPublicKey == null ? "null" : "present")
|
||||||
.append(", adminPrivateKey=").append(adminPrivateKey == null ? "null" : "present")
|
.append(", adminPrivateKey=").append(adminPrivateKey == null ? "null" : "present")
|
||||||
.append(", adminPassword=").append(adminPassword == null ? "null" : "present")
|
.append(", adminPassword=").append(adminPassword == null ? "null" : "present")
|
||||||
|
@ -368,6 +382,7 @@ public class AdminAccess implements Statement {
|
||||||
Builder builder = AdminAccess.builder(configuration.cryptFunction());
|
Builder builder = AdminAccess.builder(configuration.cryptFunction());
|
||||||
builder.adminUsername(config.getAdminUsername() != null ? config.getAdminUsername() : configuration
|
builder.adminUsername(config.getAdminUsername() != null ? config.getAdminUsername() : configuration
|
||||||
.defaultAdminUsername().get());
|
.defaultAdminUsername().get());
|
||||||
|
builder.adminFullName(config.getAdminFullName() != null ? config.getAdminFullName() : builder.adminUsername);
|
||||||
builder.adminHome(config.getAdminHome());
|
builder.adminHome(config.getAdminHome());
|
||||||
builder.adminPassword(config.getAdminPassword() != null ? config.getAdminPassword() : configuration
|
builder.adminPassword(config.getAdminPassword() != null ? config.getAdminPassword() : configuration
|
||||||
.passwordGenerator().get());
|
.passwordGenerator().get());
|
||||||
|
@ -408,6 +423,7 @@ public class AdminAccess implements Statement {
|
||||||
ImmutableList.Builder<Statement> statements = ImmutableList.builder();
|
ImmutableList.Builder<Statement> statements = ImmutableList.builder();
|
||||||
UserAdd.Builder userBuilder = UserAdd.builder();
|
UserAdd.Builder userBuilder = UserAdd.builder();
|
||||||
userBuilder.login(config.getAdminUsername());
|
userBuilder.login(config.getAdminUsername());
|
||||||
|
userBuilder.fullName(config.getAdminFullName());
|
||||||
if (config.getAdminHome() != null)
|
if (config.getAdminHome() != null)
|
||||||
userBuilder.home(config.getAdminHome());
|
userBuilder.home(config.getAdminHome());
|
||||||
if (config.shouldAuthorizeAdminPublicKey())
|
if (config.shouldAuthorizeAdminPublicKey())
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import com.google.common.base.*;
|
||||||
import org.jclouds.crypto.Sha512Crypt;
|
import org.jclouds.crypto.Sha512Crypt;
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
import org.jclouds.javax.annotation.Nullable;
|
||||||
import org.jclouds.scriptbuilder.domain.OsFamily;
|
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||||
|
@ -35,14 +36,11 @@ import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys;
|
||||||
import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
|
import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import org.jclouds.util.Strings2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a statement that will add a given user to a machine ("login"), with optional
|
* Creates a statement that will add a given user to a machine ("login"), with optional
|
||||||
|
@ -69,6 +67,7 @@ public class UserAdd implements Statement {
|
||||||
private List<String> groups = Lists.newArrayList();
|
private List<String> groups = Lists.newArrayList();
|
||||||
private List<String> authorizeRSAPublicKeys = Lists.newArrayList();
|
private List<String> authorizeRSAPublicKeys = Lists.newArrayList();
|
||||||
private String shell = "/bin/bash";
|
private String shell = "/bin/bash";
|
||||||
|
private String fullName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See --home in `man useradd`.
|
* See --home in `man useradd`.
|
||||||
|
@ -128,18 +127,22 @@ public class UserAdd implements Statement {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserAdd.Builder fullName(String fullName) {
|
||||||
|
this.fullName = fullName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
public UserAdd build() {
|
public UserAdd build() {
|
||||||
return new UserAdd(login, groups, password, RSAPrivateKey, authorizeRSAPublicKeys, home, defaultHome, shell);
|
return new UserAdd(login, groups, password, RSAPrivateKey, authorizeRSAPublicKeys, home, defaultHome, shell, fullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserAdd(String login, List<String> groups, @Nullable String password, @Nullable String installRSAPrivateKey,
|
public UserAdd(String login, List<String> groups, @Nullable String password, @Nullable String installRSAPrivateKey,
|
||||||
List<String> authorizeRSAPublicKeys, String defaultHome, String shell) {
|
List<String> authorizeRSAPublicKeys, String defaultHome, String shell) {
|
||||||
this(login, groups, password, installRSAPrivateKey, authorizeRSAPublicKeys, null, defaultHome, shell);
|
this(login, groups, password, installRSAPrivateKey, authorizeRSAPublicKeys, null, defaultHome, shell, login);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserAdd(String login, List<String> groups, @Nullable String password, @Nullable String installRSAPrivateKey,
|
public UserAdd(String login, List<String> groups, @Nullable String password, @Nullable String installRSAPrivateKey,
|
||||||
List<String> authorizeRSAPublicKeys, @Nullable String home, String defaultHome, String shell) {
|
List<String> authorizeRSAPublicKeys, @Nullable String home, String defaultHome, String shell, String fullName) {
|
||||||
this.login = checkNotNull(login, "login");
|
this.login = checkNotNull(login, "login");
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.groups = ImmutableList.copyOf(checkNotNull(groups, "groups"));
|
this.groups = ImmutableList.copyOf(checkNotNull(groups, "groups"));
|
||||||
|
@ -149,6 +152,7 @@ public class UserAdd implements Statement {
|
||||||
this.home = home;
|
this.home = home;
|
||||||
this.defaultHome = checkNotNull(defaultHome, "defaultHome");
|
this.defaultHome = checkNotNull(defaultHome, "defaultHome");
|
||||||
this.shell = checkNotNull(shell, "shell");
|
this.shell = checkNotNull(shell, "shell");
|
||||||
|
this.fullName = fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String home;
|
private final String home;
|
||||||
|
@ -159,6 +163,7 @@ public class UserAdd implements Statement {
|
||||||
private final String installRSAPrivateKey;
|
private final String installRSAPrivateKey;
|
||||||
private final List<String> authorizeRSAPublicKeys;
|
private final List<String> authorizeRSAPublicKeys;
|
||||||
private final String shell;
|
private final String shell;
|
||||||
|
private final String fullName;
|
||||||
|
|
||||||
private Function<String, String> cryptFunction = Sha512Crypt.function();
|
private Function<String, String> cryptFunction = Sha512Crypt.function();
|
||||||
|
|
||||||
|
@ -187,7 +192,12 @@ public class UserAdd implements Statement {
|
||||||
|
|
||||||
ImmutableMap.Builder<String, String> userAddOptions = ImmutableMap.builder();
|
ImmutableMap.Builder<String, String> userAddOptions = ImmutableMap.builder();
|
||||||
// Include the username as the full name for now.
|
// Include the username as the full name for now.
|
||||||
|
|
||||||
|
if (Strings.isNullOrEmpty(fullName)) {
|
||||||
userAddOptions.put("-c", login);
|
userAddOptions.put("-c", login);
|
||||||
|
} else {
|
||||||
|
userAddOptions.put("-c", "'" + fullName + "'");
|
||||||
|
}
|
||||||
|
|
||||||
userAddOptions.put("-s", shell);
|
userAddOptions.put("-s", shell);
|
||||||
if (groups.size() > 0) {
|
if (groups.size() > 0) {
|
||||||
|
|
|
@ -52,15 +52,34 @@ public class AdminAccessTest {
|
||||||
try {
|
try {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey")
|
AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey")
|
||||||
.adminPublicKey("fooPublicKey").adminUsername("foo").adminHome("/over/ridden/foo").build()
|
.adminPublicKey("fooPublicKey").adminUsername("foo")
|
||||||
|
.adminHome("/over/ridden/foo").build()
|
||||||
.init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
|
.init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
|
||||||
CharStreams.toString(Resources.newReaderSupplier(
|
CharStreams.toString(Resources.newReaderSupplier(
|
||||||
Resources.getResource("test_adminaccess_params.sh"), Charsets.UTF_8)));
|
Resources.getResource("test_adminaccess_params.sh"), Charsets.UTF_8)));
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
TestConfiguration.INSTANCE.reset();
|
TestConfiguration.INSTANCE.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWithParamsAndFullNameUNIX() throws IOException {
|
||||||
|
TestConfiguration.INSTANCE.reset();
|
||||||
|
try {
|
||||||
|
assertEquals(
|
||||||
|
AdminAccess.builder().adminPassword("bar").adminPrivateKey("fooPrivateKey")
|
||||||
|
.adminPublicKey("fooPublicKey").adminUsername("foo").adminFullName("JClouds Foo")
|
||||||
|
.adminHome("/over/ridden/foo").build()
|
||||||
|
.init(TestConfiguration.INSTANCE).render(OsFamily.UNIX),
|
||||||
|
CharStreams.toString(Resources.newReaderSupplier(
|
||||||
|
Resources.getResource("test_adminaccess_params_and_fullname.sh"), Charsets.UTF_8)));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
TestConfiguration.INSTANCE.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testOnlyInstallUserUNIX() throws IOException {
|
public void testOnlyInstallUserUNIX() throws IOException {
|
||||||
TestConfiguration.INSTANCE.reset();
|
TestConfiguration.INSTANCE.reset();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.jclouds.scriptbuilder.statements.login;
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.jclouds.JcloudsVersion;
|
||||||
import org.jclouds.scriptbuilder.domain.OsFamily;
|
import org.jclouds.scriptbuilder.domain.OsFamily;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@ -36,6 +37,12 @@ public class UserAddTest {
|
||||||
"mkdir -p /home/users\nuseradd -c me -s /bin/bash -m -d /home/users/me me\nchown -R me /home/users/me\n");
|
"mkdir -p /home/users\nuseradd -c me -s /bin/bash -m -d /home/users/me me\nchown -R me /home/users/me\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWithFullNameUNIX() {
|
||||||
|
assertEquals(UserAdd.builder().login("me").fullName("JClouds Guy").build().render(OsFamily.UNIX),
|
||||||
|
"mkdir -p /home/users\nuseradd -c 'JClouds Guy' -s /bin/bash -m -d /home/users/me me\nchown -R me /home/users/me\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void testWithBaseUNIX() {
|
public void testWithBaseUNIX() {
|
||||||
assertEquals(UserAdd.builder().login("me").defaultHome("/export/home").build().render(OsFamily.UNIX),
|
assertEquals(UserAdd.builder().login("me").defaultHome("/export/home").build().render(OsFamily.UNIX),
|
||||||
"mkdir -p /export/home\nuseradd -c me -s /bin/bash -m -d /export/home/me me\nchown -R me /export/home/me\n");
|
"mkdir -p /export/home\nuseradd -c me -s /bin/bash -m -d /export/home/me me\nchown -R me /export/home/me\n");
|
||||||
|
|
|
@ -5,7 +5,7 @@ END_OF_JCLOUDS_FILE
|
||||||
chmod 0440 /etc/sudoers
|
chmod 0440 /etc/sudoers
|
||||||
mkdir -p /over/ridden
|
mkdir -p /over/ridden
|
||||||
groupadd -f wheel
|
groupadd -f wheel
|
||||||
useradd -c foo -s /bin/bash -g wheel -m -d /over/ridden/foo -p 'crypt(bar)' foo
|
useradd -c 'foo' -s /bin/bash -g wheel -m -d /over/ridden/foo -p 'crypt(bar)' foo
|
||||||
mkdir -p /over/ridden/foo/.ssh
|
mkdir -p /over/ridden/foo/.ssh
|
||||||
cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
||||||
fooPublicKey
|
fooPublicKey
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
cat > /etc/sudoers <<-'END_OF_JCLOUDS_FILE'
|
||||||
|
root ALL = (ALL) ALL
|
||||||
|
%wheel ALL = (ALL) NOPASSWD:ALL
|
||||||
|
END_OF_JCLOUDS_FILE
|
||||||
|
chmod 0440 /etc/sudoers
|
||||||
|
mkdir -p /over/ridden
|
||||||
|
groupadd -f wheel
|
||||||
|
useradd -c 'JClouds Foo' -s /bin/bash -g wheel -m -d /over/ridden/foo -p 'crypt(bar)' foo
|
||||||
|
mkdir -p /over/ridden/foo/.ssh
|
||||||
|
cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
||||||
|
fooPublicKey
|
||||||
|
END_OF_JCLOUDS_FILE
|
||||||
|
chmod 600 /over/ridden/foo/.ssh/authorized_keys
|
||||||
|
chown -R foo /over/ridden/foo
|
||||||
|
exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
|
||||||
|
PermitRootLogin no
|
||||||
|
" 'BEGIN {print TEXT}{print}' /etc/ssh/sshd_config >&3
|
||||||
|
hash service 2>&- && service ssh reload 2>&- || /etc/init.d/ssh* reload
|
||||||
|
awk -v user=^${SUDO_USER:=${USER}}: -v password='crypt(0)' 'BEGIN { FS=OFS=":" } $0 ~ user { $2 = password } 1' /etc/shadow >/etc/shadow.${SUDO_USER:=${USER}}
|
||||||
|
test -f /etc/shadow.${SUDO_USER:=${USER}} && mv /etc/shadow.${SUDO_USER:=${USER}} /etc/shadow
|
|
@ -1,5 +1,5 @@
|
||||||
mkdir -p /home/users
|
mkdir -p /home/users
|
||||||
useradd -c defaultAdminUsername -s /bin/bash -m -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
useradd -c 'defaultAdminUsername' -s /bin/bash -m -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
||||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||||
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
||||||
publicKey
|
publicKey
|
||||||
|
|
|
@ -5,7 +5,7 @@ END_OF_JCLOUDS_FILE
|
||||||
chmod 0440 /etc/sudoers
|
chmod 0440 /etc/sudoers
|
||||||
mkdir -p /home/users
|
mkdir -p /home/users
|
||||||
groupadd -f wheel
|
groupadd -f wheel
|
||||||
useradd -c defaultAdminUsername -s /bin/bash -g wheel -m -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
useradd -c 'defaultAdminUsername' -s /bin/bash -g wheel -m -d /home/users/defaultAdminUsername -p 'crypt(0)' defaultAdminUsername
|
||||||
mkdir -p /home/users/defaultAdminUsername/.ssh
|
mkdir -p /home/users/defaultAdminUsername/.ssh
|
||||||
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
|
||||||
publicKey
|
publicKey
|
||||||
|
|
Loading…
Reference in New Issue