Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
6e773c84e8
23 changed files with 302 additions and 353 deletions
|
@ -71,9 +71,9 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
||||||
@Option(shortName = 't', name = "token", description = "Initial / Registration access token to use)", hasValue = true)
|
@Option(shortName = 't', name = "token", description = "Initial / Registration access token to use)", hasValue = true)
|
||||||
protected String token;
|
protected String token;
|
||||||
|
|
||||||
protected void init(AbstractAuthOptionsCmd parent) {
|
protected void initFromParent(AbstractAuthOptionsCmd parent) {
|
||||||
|
|
||||||
super.init(parent);
|
super.initFromParent(parent);
|
||||||
|
|
||||||
noconfig = parent.noconfig;
|
noconfig = parent.noconfig;
|
||||||
config = parent.config;
|
config = parent.config;
|
||||||
|
@ -159,7 +159,8 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
||||||
initConfigData(config);
|
initConfigData(config);
|
||||||
ConfigUtil.setupInMemoryHandler(config);
|
ConfigUtil.setupInMemoryHandler(config);
|
||||||
|
|
||||||
ConfigCredentialsCmd login = new ConfigCredentialsCmd(this);
|
ConfigCredentialsCmd login = new ConfigCredentialsCmd();
|
||||||
|
login.initFromParent(this);
|
||||||
login.init(config);
|
login.init(config);
|
||||||
login.process(commandInvocation);
|
login.process(commandInvocation);
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ public abstract class AbstractGlobalOptionsCmd implements Command {
|
||||||
@Option(name = "help", description = "Print command specific help", hasValue = false)
|
@Option(name = "help", description = "Print command specific help", hasValue = false)
|
||||||
protected boolean help;
|
protected boolean help;
|
||||||
|
|
||||||
protected void init(AbstractGlobalOptionsCmd parent) {
|
protected void initFromParent(AbstractGlobalOptionsCmd parent) {
|
||||||
dumpTrace = parent.dumpTrace;
|
dumpTrace = parent.dumpTrace;
|
||||||
help = parent.help;
|
help = parent.help;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,16 +53,24 @@ public class ConfigCmd extends AbstractAuthOptionsCmd implements Command {
|
||||||
String cmd = args.get(0);
|
String cmd = args.get(0);
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case "credentials": {
|
case "credentials": {
|
||||||
return new ConfigCredentialsCmd(this).execute(commandInvocation);
|
ConfigCredentialsCmd command = new ConfigCredentialsCmd();
|
||||||
|
command.initFromParent(this);
|
||||||
|
return command.execute(commandInvocation);
|
||||||
}
|
}
|
||||||
case "truststore": {
|
case "truststore": {
|
||||||
return new ConfigTruststoreCmd(this).execute(commandInvocation);
|
ConfigTruststoreCmd command = new ConfigTruststoreCmd();
|
||||||
|
command.initFromParent(this);
|
||||||
|
return command.execute(commandInvocation);
|
||||||
}
|
}
|
||||||
case "initial-token": {
|
case "initial-token": {
|
||||||
return new ConfigInitialTokenCmd(this).execute(commandInvocation);
|
ConfigInitialTokenCmd command = new ConfigInitialTokenCmd();
|
||||||
|
command.initFromParent(this);
|
||||||
|
return command.execute(commandInvocation);
|
||||||
}
|
}
|
||||||
case "registration-token": {
|
case "registration-token": {
|
||||||
return new ConfigRegistrationTokenCmd(this).execute(commandInvocation);
|
ConfigRegistrationTokenCmd command = new ConfigRegistrationTokenCmd();
|
||||||
|
command.initFromParent(this);
|
||||||
|
return command.execute(commandInvocation);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
if (printHelp()) {
|
if (printHelp()) {
|
||||||
|
|
|
@ -36,11 +36,6 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
||||||
|
|
||||||
private int sigLifetime = 600;
|
private int sigLifetime = 600;
|
||||||
|
|
||||||
public ConfigCredentialsCmd() {}
|
|
||||||
|
|
||||||
public ConfigCredentialsCmd(AbstractAuthOptionsCmd parent) {
|
|
||||||
init(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(ConfigData configData) {
|
public void init(ConfigData configData) {
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
|
|
|
@ -33,11 +33,10 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd implements Com
|
||||||
private boolean delete;
|
private boolean delete;
|
||||||
private boolean keepDomain;
|
private boolean keepDomain;
|
||||||
|
|
||||||
public ConfigInitialTokenCmd() {}
|
|
||||||
|
|
||||||
public ConfigInitialTokenCmd(ConfigCmd parent) {
|
protected void initFromParent(ConfigCmd parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
init(parent);
|
super.initFromParent(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,11 +30,10 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd implement
|
||||||
|
|
||||||
private boolean delete;
|
private boolean delete;
|
||||||
|
|
||||||
public ConfigRegistrationTokenCmd() {}
|
|
||||||
|
|
||||||
public ConfigRegistrationTokenCmd(ConfigCmd parent) {
|
protected void initFromParent(ConfigCmd parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
init(parent);
|
super.initFromParent(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,11 +30,10 @@ public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd implements Comma
|
||||||
|
|
||||||
private boolean delete;
|
private boolean delete;
|
||||||
|
|
||||||
public ConfigTruststoreCmd() {}
|
|
||||||
|
|
||||||
public ConfigTruststoreCmd(ConfigCmd parent) {
|
protected void initFromParent(ConfigCmd parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
init(parent);
|
super.initFromParent(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1746,6 +1746,10 @@ public class RepresentationToModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateComponent(KeycloakSession session, ComponentRepresentation rep, ComponentModel component, boolean internal) {
|
public static void updateComponent(KeycloakSession session, ComponentRepresentation rep, ComponentModel component, boolean internal) {
|
||||||
|
if (rep.getName() != null) {
|
||||||
|
component.setName(rep.getName());
|
||||||
|
}
|
||||||
|
|
||||||
if (rep.getParentId() != null) {
|
if (rep.getParentId() != null) {
|
||||||
component.setParentId(rep.getParentId());
|
component.setParentId(rep.getParentId());
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,24 @@ public class ComponentsTest extends AbstractAdminTest {
|
||||||
assertEquals(1, returned.getConfig().size());
|
assertEquals(1, returned.getConfig().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRename() {
|
||||||
|
ComponentRepresentation rep = createComponentRepresentation("mycomponent");
|
||||||
|
rep.getConfig().addFirst("required", "foo");
|
||||||
|
|
||||||
|
String id = createComponent(rep);
|
||||||
|
ComponentRepresentation returned = components.component(id).toRepresentation();
|
||||||
|
assertEquals("mycomponent", returned.getName());
|
||||||
|
|
||||||
|
rep.setName("myupdatedcomponent");
|
||||||
|
|
||||||
|
components.component(id).update(rep);
|
||||||
|
|
||||||
|
returned = components.component(id).toRepresentation();
|
||||||
|
assertEquals("myupdatedcomponent", returned.getName());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSecretConfig() throws Exception {
|
public void testSecretConfig() throws Exception {
|
||||||
ComponentRepresentation rep = createComponentRepresentation("mycomponent");
|
ComponentRepresentation rep = createComponentRepresentation("mycomponent");
|
||||||
|
|
|
@ -539,17 +539,30 @@ public abstract class AbstractCliTest extends AbstractKeycloakTest {
|
||||||
Assert.assertEquals("exitCode == " + exitCode, exitCode, exe.exitCode());
|
Assert.assertEquals("exitCode == " + exitCode, exitCode, exe.exitCode());
|
||||||
if (stdOutLineCount != -1) {
|
if (stdOutLineCount != -1) {
|
||||||
try {
|
try {
|
||||||
Assert.assertTrue("stdout output has " + stdOutLineCount + " lines", exe.stdoutLines().size() == stdOutLineCount);
|
assertLineCount("stdout output", exe.stdoutLines(), stdOutLineCount);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new AssertionError("STDOUT: " + exe.stdoutString(), e);
|
throw new AssertionError("STDOUT: " + exe.stdoutString(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stdErrLineCount != -1) {
|
if (stdErrLineCount != -1) {
|
||||||
try {
|
try {
|
||||||
Assert.assertTrue("stderr output has " + stdErrLineCount + " lines", exe.stderrLines().size() == stdErrLineCount);
|
assertLineCount("stderr output", exe.stderrLines(), stdErrLineCount);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new AssertionError("STDERR: " + exe.stderrString(), e);
|
throw new AssertionError("STDERR: " + exe.stderrString(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assertLineCount(String label, List<String> lines, int count) {
|
||||||
|
if (lines.size() == count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// there is some kind of race condition in 'kcreg' that results in intermittent extra empty line
|
||||||
|
if (lines.size() == count + 1) {
|
||||||
|
if ("".equals(lines.get(lines.size()-1))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.assertTrue(label + " has " + lines.size() + " lines (expected: " + count + ")", lines.size() == count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,13 +32,13 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
KcRegExec exe = execute("config credentials -x --config '" + configFile.getName() +
|
KcRegExec exe = execute("config credentials -x --config '" + configFile.getName() +
|
||||||
"' --server " + serverUrl + " --realm master --user admin --password admin");
|
"' --server " + serverUrl + " --realm master --user admin --password admin");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 0, 0, 1);
|
||||||
|
|
||||||
// use initial token of another realm with server, and realm override
|
// use initial token of another realm with server, and realm override
|
||||||
String token = issueInitialAccessToken("test");
|
String token = issueInitialAccessToken("test");
|
||||||
exe = execute("create --config '" + configFile.getName() + "' --server " + serverUrl + " --realm test -s clientId=my_first_client -t " + token);
|
exe = execute("create --config '" + configFile.getName() + "' --server " + serverUrl + " --realm test -s clientId=my_first_client -t " + token);
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 0, 0, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
KcRegExec exe = execute("config initial-token -x --config '" + configFile.getName() +
|
KcRegExec exe = execute("config initial-token -x --config '" + configFile.getName() +
|
||||||
"' --server " + serverUrl + " --realm " + realm + " " + token);
|
"' --server " + serverUrl + " --realm " + realm + " " + token);
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||||
|
|
||||||
// check that current server, realm, and initial token are saved in the file
|
// check that current server, realm, and initial token are saved in the file
|
||||||
ConfigData config = handler.loadConfig();
|
ConfigData config = handler.loadConfig();
|
||||||
|
@ -87,8 +87,7 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
|
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -o -f - < '" + tmpFile.getName() + "'");
|
exe = execute("create --config '" + configFile.getName() + "' -o -f - < '" + tmpFile.getName() + "'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertNotNull("id", client.getId());
|
Assert.assertNotNull("id", client.getId());
|
||||||
|
@ -114,8 +113,7 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
" -s 'name=My Client App II' -s protocol=keycloak-oidc -s 'webOrigins=[\"http://localhost:8980/myapp2\"]'" +
|
" -s 'name=My Client App II' -s protocol=keycloak-oidc -s 'webOrigins=[\"http://localhost:8980/myapp2\"]'" +
|
||||||
" -s baseUrl=http://localhost:8980/myapp2 -s rootUrl=http://localhost:8980/myapp2");
|
" -s baseUrl=http://localhost:8980/myapp2 -s rootUrl=http://localhost:8980/myapp2");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
ClientRepresentation client2 = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
ClientRepresentation client2 = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertNotNull("id", client2.getId());
|
Assert.assertNotNull("id", client2.getId());
|
||||||
|
@ -138,29 +136,22 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
// check that using an invalid attribute key is not ignored
|
// check that using an invalid attribute key is not ignored
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -o -f '" + tmpFile.getName() + "' -s client_id=my_client3");
|
exe = execute("create --config '" + configFile.getName() + "' -o -f '" + tmpFile.getName() + "' -s client_id=my_client3");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 1", 1, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 1, 0, 1);
|
||||||
Assert.assertEquals("stderr has one line", 1, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("Failed to set attribute 'client_id' on document type 'default'", exe.stderrLines().get(0));
|
Assert.assertEquals("Failed to set attribute 'client_id' on document type 'default'", exe.stderrLines().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// simple create, output an id
|
// simple create, output an id
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -i -s clientId=my_client3");
|
exe = execute("create --config '" + configFile.getName() + "' -i -s clientId=my_client3");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 0, 1, 0);
|
||||||
Assert.assertEquals("stderr is empty", 0, exe.stderrLines().size());
|
|
||||||
|
|
||||||
Assert.assertEquals("stdout has 1 line", 1, exe.stdoutLines().size());
|
|
||||||
Assert.assertEquals("only clientId returned", "my_client3", exe.stdoutLines().get(0));
|
Assert.assertEquals("only clientId returned", "my_client3", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
// simple create, default output
|
// simple create, default output
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -s clientId=my_client4");
|
exe = execute("create --config '" + configFile.getName() + "' -s clientId=my_client4");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 0, 0, 1);
|
||||||
Assert.assertEquals("stderr has 1 line", 1, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("only clientId returned", "Registered new client with client_id 'my_client4'", exe.stderrLines().get(0));
|
Assert.assertEquals("only clientId returned", "Registered new client with client_id 'my_client4'", exe.stderrLines().get(0));
|
||||||
|
|
||||||
Assert.assertEquals("stdout is empty", 0, exe.stdoutLines().size());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// create using oidc endpoint - autodetect format
|
// create using oidc endpoint - autodetect format
|
||||||
|
@ -178,8 +169,7 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
" -s 'redirect_uris=[\"http://localhost:8980/myapp5/*\"]' -s client_uri=http://localhost:8980/myapp5" +
|
" -s 'redirect_uris=[\"http://localhost:8980/myapp5/*\"]' -s client_uri=http://localhost:8980/myapp5" +
|
||||||
" -o -f - < '" + tmpFile.getName() + "'");
|
" -o -f - < '" + tmpFile.getName() + "'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
OIDCClientRepresentation client = JsonSerialization.readValue(exe.stdout(), OIDCClientRepresentation.class);
|
OIDCClientRepresentation client = JsonSerialization.readValue(exe.stdout(), OIDCClientRepresentation.class);
|
||||||
|
|
||||||
|
@ -195,10 +185,8 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
// try use incompatible endpoint override
|
// try use incompatible endpoint override
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -e default -f '" + tmpFile.getName() + "'");
|
exe = execute("create --config '" + configFile.getName() + "' -e default -f '" + tmpFile.getName() + "'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 1", 1, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 1, 0, 1);
|
||||||
Assert.assertFalse("stderr not empty", exe.stderrLines().isEmpty());
|
|
||||||
Assert.assertEquals("Error message", "Attribute 'redirect_uris' not supported on document type 'default'", exe.stderrLines().get(0));
|
Assert.assertEquals("Error message", "Attribute 'redirect_uris' not supported on document type 'default'", exe.stderrLines().get(0));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -208,8 +196,7 @@ public class KcRegCreateTest extends AbstractCliTest {
|
||||||
|
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -o -f - < '" + samlSpMetaFile.getAbsolutePath() + "'");
|
exe = execute("create --config '" + configFile.getName() + "' -o -f - < '" + samlSpMetaFile.getAbsolutePath() + "'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertNotNull("id", client.getId());
|
Assert.assertNotNull("id", client.getId());
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
*/
|
*/
|
||||||
KcRegExec exe = execute("");
|
KcRegExec exe = execute("");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode", 1, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||||
|
|
||||||
List<String> lines = exe.stdoutLines();
|
List<String> lines = exe.stdoutLines();
|
||||||
Assert.assertTrue("stdout output not empty", lines.size() > 0);
|
Assert.assertTrue("stdout output not empty", lines.size() > 0);
|
||||||
|
@ -41,8 +41,6 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
Assert.assertEquals("stdout one but last line", "Use '" + KcRegExec.CMD + " help <command>' for more information about a given command.", lines.get(lines.size() - 2));
|
Assert.assertEquals("stdout one but last line", "Use '" + KcRegExec.CMD + " help <command>' for more information about a given command.", lines.get(lines.size() - 2));
|
||||||
Assert.assertEquals("stdout last line", "", lines.get(lines.size() - 1));
|
Assert.assertEquals("stdout last line", "", lines.get(lines.size() - 1));
|
||||||
|
|
||||||
lines = exe.stderrLines();
|
|
||||||
Assert.assertTrue("stderr output empty", lines.size() == 0);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test commands without arguments
|
* Test commands without arguments
|
||||||
|
@ -79,7 +77,7 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
Assert.assertEquals("error message", "CLIENT not specified", exe.stderrLines().get(0));
|
Assert.assertEquals("error message", "CLIENT not specified", exe.stderrLines().get(0));
|
||||||
|
|
||||||
exe = execute("help");
|
exe = execute("help");
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
lines = exe.stdoutLines();
|
lines = exe.stdoutLines();
|
||||||
Assert.assertTrue("stdout output not empty", lines.size() > 0);
|
Assert.assertTrue("stdout output not empty", lines.size() > 0);
|
||||||
Assert.assertEquals("stdout first line", "Keycloak Client Registration CLI", lines.get(0));
|
Assert.assertEquals("stdout first line", "Keycloak Client Registration CLI", lines.get(0));
|
||||||
|
@ -93,69 +91,57 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
* Test --help for all commands
|
* Test --help for all commands
|
||||||
*/
|
*/
|
||||||
KcRegExec exe = execute("--help");
|
KcRegExec exe = execute("--help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Keycloak Client Registration CLI", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Keycloak Client Registration CLI", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("create --help");
|
exe = execute("create --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + CMD + " create [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + CMD + " create [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("get --help");
|
exe = execute("get --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + CMD + " get CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + CMD + " get CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("update --help");
|
exe = execute("update --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + CMD + " update CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + CMD + " update CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("delete --help");
|
exe = execute("delete --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + CMD + " delete CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + CMD + " delete CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("attrs --help");
|
exe = execute("attrs --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + CMD + " attrs [ATTRIBUTE] [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + CMD + " attrs [ATTRIBUTE] [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("update-token --help");
|
exe = execute("update-token --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + CMD + " update-token CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + CMD + " update-token CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("config --help");
|
exe = execute("config --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line", "Usage: " + OsUtil.CMD + " config SUB_COMMAND [ARGUMENTS]", exe.stdoutLines().get(0));
|
Assert.assertEquals("stdout first line", "Usage: " + OsUtil.CMD + " config SUB_COMMAND [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("config credentials --help");
|
exe = execute("config credentials --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line",
|
Assert.assertEquals("stdout first line",
|
||||||
"Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM [ARGUMENTS]",
|
"Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM [ARGUMENTS]",
|
||||||
exe.stdoutLines().get(0));
|
exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("config initial-token --help");
|
exe = execute("config initial-token --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line",
|
Assert.assertEquals("stdout first line",
|
||||||
"Usage: " + CMD + " config initial-token --server SERVER --realm REALM [--delete | TOKEN] [ARGUMENTS]",
|
"Usage: " + CMD + " config initial-token --server SERVER --realm REALM [--delete | TOKEN] [ARGUMENTS]",
|
||||||
exe.stdoutLines().get(0));
|
exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("config registration-token --help");
|
exe = execute("config registration-token --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line",
|
Assert.assertEquals("stdout first line",
|
||||||
"Usage: " + CMD + " config registration-token --server SERVER --realm REALM --client CLIENT [--delete | TOKEN] [ARGUMENTS]",
|
"Usage: " + CMD + " config registration-token --server SERVER --realm REALM --client CLIENT [--delete | TOKEN] [ARGUMENTS]",
|
||||||
exe.stdoutLines().get(0));
|
exe.stdoutLines().get(0));
|
||||||
|
|
||||||
exe = execute("config truststore --help");
|
exe = execute("config truststore --help");
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertEquals("no stderr", 0, exe.stderrLines().size());
|
|
||||||
Assert.assertEquals("stdout first line",
|
Assert.assertEquals("stdout first line",
|
||||||
"Usage: " + CMD + " config truststore [TRUSTSTORE | --delete] [--trustpass PASSWOD] [ARGUMENTS]",
|
"Usage: " + CMD + " config truststore [TRUSTSTORE | --delete] [--trustpass PASSWOD] [ARGUMENTS]",
|
||||||
exe.stdoutLines().get(0));
|
exe.stdoutLines().get(0));
|
||||||
|
@ -379,7 +365,7 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
KcRegExec exe = execute("config credentials --server " + serverUrl +
|
KcRegExec exe = execute("config credentials --server " + serverUrl +
|
||||||
" --realm master --user admin --password admin --config '" + configFile.getName() + "'");
|
" --realm master --user admin --password admin --config '" + configFile.getName() + "'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStreamSizes(exe, 0, 0, 1);
|
||||||
|
|
||||||
// remember the state of config file
|
// remember the state of config file
|
||||||
ConfigData config1 = handler.loadConfig();
|
ConfigData config1 = handler.loadConfig();
|
||||||
|
@ -389,7 +375,7 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
|
|
||||||
exe = execute("create --config '" + configFile.getName() + "' -s clientId=test-client -o");
|
exe = execute("create --config '" + configFile.getName() + "' -s clientId=test-client -o");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
|
|
||||||
// check changes to config file
|
// check changes to config file
|
||||||
ConfigData config2 = handler.loadConfig();
|
ConfigData config2 = handler.loadConfig();
|
||||||
|
@ -619,12 +605,17 @@ public class KcRegTest extends AbstractCliTest {
|
||||||
KcRegExec exe = execute("create " + (useConfig ? ("--config '" + configFile.getAbsolutePath()) + "'" : "--no-config")
|
KcRegExec exe = execute("create " + (useConfig ? ("--config '" + configFile.getAbsolutePath()) + "'" : "--no-config")
|
||||||
+ " --server " + serverUrl + " --realm " + realm + " -s clientId=test-client -o");
|
+ " --server " + serverUrl + " --realm " + realm + " -s clientId=test-client -o");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
|
|
||||||
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
|
|
||||||
Assert.assertEquals("clientId", "test-client", client.getClientId());
|
Assert.assertEquals("clientId", "test-client", client.getClientId());
|
||||||
Assert.assertNotNull("registrationAccessToken", client.getRegistrationAccessToken());
|
Assert.assertNotNull("registrationAccessToken", client.getRegistrationAccessToken());
|
||||||
|
|
||||||
|
exe = execute("delete test-client " + (useConfig ? ("--config '" + configFile.getAbsolutePath()) + "'" : "--no-config")
|
||||||
|
+ " --server " + serverUrl + " --realm " + realm + " -t " + client.getRegistrationAccessToken());
|
||||||
|
|
||||||
|
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
// create an object so we can update it
|
// create an object so we can update it
|
||||||
KcRegExec exe = execute("create --config '" + configFile.getName() + "' -o -s clientId=my_client");
|
KcRegExec exe = execute("create --config '" + configFile.getName() + "' -o -s clientId=my_client");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
|
|
||||||
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
|
|
||||||
Assert.assertEquals("enabled", true, client.isEnabled());
|
Assert.assertEquals("enabled", true, client.isEnabled());
|
||||||
|
@ -48,8 +49,7 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
exe = execute("update my_client --config '" + configFile.getName() + "' -o " +
|
exe = execute("update my_client --config '" + configFile.getName() + "' -o " +
|
||||||
" -s enabled=false -s 'redirectUris=[\"http://localhost:8980/myapp/*\"]'");
|
" -s enabled=false -s 'redirectUris=[\"http://localhost:8980/myapp/*\"]'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertEquals("enabled", false, client.isEnabled());
|
Assert.assertEquals("enabled", false, client.isEnabled());
|
||||||
|
@ -60,8 +60,7 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
// Another merge update - test deleting an attribute, deleting a list item and adding a list item
|
// Another merge update - test deleting an attribute, deleting a list item and adding a list item
|
||||||
exe = execute("update my_client --config '" + configFile.getName() + "' -o -d redirectUris -s webOrigins+=http://localhost:8980/myapp -s webOrigins+=http://localhost:8981/myapp -d webOrigins[0]");
|
exe = execute("update my_client --config '" + configFile.getName() + "' -o -d redirectUris -s webOrigins+=http://localhost:8980/myapp -s webOrigins+=http://localhost:8981/myapp -d webOrigins[0]");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
|
|
||||||
|
@ -76,8 +75,7 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
exe = execute("update my_client --config '" + configFile.getName() + "' -o -s 'protocolMappers[0].config.\"id.token.claim\"=false' " +
|
exe = execute("update my_client --config '" + configFile.getName() + "' -o -s 'protocolMappers[0].config.\"id.token.claim\"=false' " +
|
||||||
"-s 'protocolMappers[4].config={\"single\": \"true\", \"attribute.nameformat\": \"Basic\", \"attribute.name\": \"Role\"}'");
|
"-s 'protocolMappers[4].config={\"single\": \"true\", \"attribute.nameformat\": \"Basic\", \"attribute.name\": \"Role\"}'");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertEquals("protocolMapper[0].config.\"id.token.claim\"", "false", client.getProtocolMappers().get(0).getConfig().get("id.token.claim"));
|
Assert.assertEquals("protocolMapper[0].config.\"id.token.claim\"", "false", client.getProtocolMappers().get(0).getConfig().get("id.token.claim"));
|
||||||
|
@ -112,8 +110,7 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
.stdin(new ByteArrayInputStream("{ \"enabled\": false }".getBytes()))
|
.stdin(new ByteArrayInputStream("{ \"enabled\": false }".getBytes()))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr has error", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
// web origin is not sent to the server, thus it retains the current value
|
// web origin is not sent to the server, thus it retains the current value
|
||||||
|
@ -130,8 +127,7 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
.stdin(new ByteArrayInputStream("{ \"webOrigins\": [\"http://localhost:8980/myapp\"] }".getBytes()))
|
.stdin(new ByteArrayInputStream("{ \"webOrigins\": [\"http://localhost:8980/myapp\"] }".getBytes()))
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr has error", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertEquals("webOrigins", Arrays.asList("http://localhost:8980/myapp"), client.getWebOrigins());
|
Assert.assertEquals("webOrigins", Arrays.asList("http://localhost:8980/myapp"), client.getWebOrigins());
|
||||||
|
@ -144,8 +140,7 @@ public class KcRegUpdateTest extends AbstractCliTest {
|
||||||
exe = execute("config registration-token --config '" + configFile.getName() + "' --server " + serverUrl +
|
exe = execute("config registration-token --config '" + configFile.getName() + "' --server " + serverUrl +
|
||||||
" --realm " + realm + " --client my_client -d");
|
" --realm " + realm + " --client my_client -d");
|
||||||
|
|
||||||
Assert.assertEquals("exitCode == 0", 0, exe.exitCode());
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
Assert.assertTrue("stderr is empty", exe.stderrLines().isEmpty());
|
|
||||||
|
|
||||||
Assert.assertNull("my_client registration token", handler.loadConfig().ensureRealmConfigData(serverUrl, realm).getClients().get("my_client"));
|
Assert.assertNull("my_client registration token", handler.loadConfig().ensureRealmConfigData(serverUrl, realm).getClients().get("my_client"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
|
||||||
import static org.keycloak.testsuite.cli.KcRegExec.execute;
|
import static org.keycloak.testsuite.cli.KcRegExec.execute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +58,8 @@ public class KcRegUpdateTokenTest extends AbstractCliTest {
|
||||||
|
|
||||||
// test that the token works
|
// test that the token works
|
||||||
exe = execute("get reg-cli-secret-direct --no-config --server " + serverUrl + " --realm test -t " + token);
|
exe = execute("get reg-cli-secret-direct --no-config --server " + serverUrl + " --realm test -t " + token);
|
||||||
Assert.assertEquals("exit code", 0, exe.exitCode());
|
|
||||||
|
assertExitCodeAndStdErrSize(exe, 0, 0);
|
||||||
|
|
||||||
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
ClientRepresentation client = JsonSerialization.readValue(exe.stdout(), ClientRepresentation.class);
|
||||||
Assert.assertEquals("client representation returned", "reg-cli-secret-direct", client.getClientId());
|
Assert.assertEquals("client representation returned", "reg-cli-secret-direct", client.getClientId());
|
||||||
|
|
|
@ -2,66 +2,66 @@
|
||||||
<@layout.mainLayout active='totp' bodyClass='totp'; section>
|
<@layout.mainLayout active='totp' bodyClass='totp'; section>
|
||||||
|
|
||||||
<#if totp.enabled>
|
<#if totp.enabled>
|
||||||
<h2>${msg("authenticatorTitle")}</h2>
|
<h2>${msg("authenticatorTitle")}</h2>
|
||||||
|
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<thead
|
<thead
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2">${msg("configureAuthenticators")}</th>
|
<th colspan="2">${msg("configureAuthenticators")}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="provider">${msg("mobile")}</td>
|
<td class="provider">${msg("mobile")}</td>
|
||||||
<td class="action">
|
<td class="action">
|
||||||
<a id="remove-mobile" href="${url.totpRemoveUrl}"><i class="pficon pficon-delete"></i></a>
|
<a id="remove-mobile" href="${url.totpRemoveUrl}"><i class="pficon pficon-delete"></i></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<#else>
|
<#else>
|
||||||
<h2>${msg("authenticatorTitle")}</h2>
|
<h2>${msg("authenticatorTitle")}</h2>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>
|
<li>
|
||||||
<p>${msg("totpStep1")}</p>
|
<p>${msg("totpStep1")}</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>${msg("totpStep2")}</p>
|
<p>${msg("totpStep2")}</p>
|
||||||
<img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
|
<p><img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"></p>
|
||||||
<span class="code">${totp.totpSecretEncoded}</span>
|
<p><span class="code">${totp.totpSecretEncoded}</span></p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<p>${msg("totpStep3")}</p>
|
<p>${msg("totpStep3")}</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
<form action="${url.totpUrl}" class="form-horizontal" method="post">
|
<form action="${url.totpUrl}" class="form-horizontal" method="post">
|
||||||
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker?html}">
|
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker?html}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-2 col-md-2">
|
<div class="col-sm-2 col-md-2">
|
||||||
<label for="totp" class="control-label">${msg("authenticatorCode")}</label>
|
<label for="totp" class="control-label">${msg("authenticatorCode")}</label>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-10 col-md-10">
|
|
||||||
<input type="text" class="form-control" id="totp" name="totp" autocomplete="off" autofocus autocomplete="off">
|
|
||||||
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="col-sm-10 col-md-10">
|
||||||
<div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
|
<input type="text" class="form-control" id="totp" name="totp" autocomplete="off" autofocus autocomplete="off">
|
||||||
<div class="">
|
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
|
||||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Save">${msg("doSave")}</button>
|
</div>
|
||||||
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Cancel">${msg("doCancel")}</button>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
|
||||||
|
<div class="">
|
||||||
|
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Save">${msg("doSave")}</button>
|
||||||
|
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Cancel">${msg("doCancel")}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
</form>
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
</@layout.mainLayout>
|
</@layout.mainLayout>
|
||||||
|
|
|
@ -1112,12 +1112,10 @@ module.controller('RealmKeysProvidersCtrl', function($scope, Realm, realm, $http
|
||||||
parent: realm.id,
|
parent: realm.id,
|
||||||
type: 'org.keycloak.keys.KeyProvider'
|
type: 'org.keycloak.keys.KeyProvider'
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
console.debug(data);
|
|
||||||
$scope.instances = data;
|
$scope.instances = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.addProvider = function(provider) {
|
$scope.addProvider = function(provider) {
|
||||||
console.log('Add provider: ' + provider.id);
|
|
||||||
$location.url("/create/keys/" + realm.realm + "/providers/" + provider.id);
|
$location.url("/create/keys/" + realm.realm + "/providers/" + provider.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1159,19 +1157,27 @@ module.controller('GenericKeystoreCtrl', function($scope, $location, Notificatio
|
||||||
'priority': ["0"]
|
'priority': ["0"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$scope.instance = angular.copy(instance);
|
||||||
|
}
|
||||||
|
|
||||||
if (providerFactory.properties) {
|
if (providerFactory.properties) {
|
||||||
for (var i = 0; i < providerFactory.properties.length; i++) {
|
for (var i = 0; i < providerFactory.properties.length; i++) {
|
||||||
var configProperty = providerFactory.properties[i];
|
var configProperty = providerFactory.properties[i];
|
||||||
|
if (!$scope.instance.config[configProperty.name]) {
|
||||||
if (configProperty.defaultValue) {
|
if (configProperty.defaultValue) {
|
||||||
$scope.instance.config[configProperty.name] = [configProperty.defaultValue];
|
$scope.instance.config[configProperty.name] = [configProperty.defaultValue];
|
||||||
|
if (!$scope.create) {
|
||||||
|
instance.config[configProperty.name] = [configProperty.defaultValue];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$scope.instance.config[configProperty.name] = [''];
|
$scope.instance.config[configProperty.name] = [''];
|
||||||
|
if (!$scope.create) {
|
||||||
|
instance.config[configProperty.name] = [configProperty.defaultValue];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$scope.instance = angular.copy(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$watch('instance', function() {
|
$scope.$watch('instance', function() {
|
||||||
|
|
|
@ -41,19 +41,18 @@
|
||||||
<div class="form-group clearfix">
|
<div class="form-group clearfix">
|
||||||
<label class="col-md-2 control-label" for="consoleDisplayName">{{:: 'console-display-name' | translate}} </label>
|
<label class="col-md-2 control-label" for="consoleDisplayName">{{:: 'console-display-name' | translate}} </label>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<input class="form-control" id="consoleDisplayName" type="text" ng-model="instance.name" placeholder="{{:: 'defaults-to-id' | translate}}">
|
<input required class="form-control" id="consoleDisplayName" type="text" ng-model="instance.name" placeholder="{{:: 'defaults-to-id' | translate}}">
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'console-display-name.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'console-display-name.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<kc-component-config realm="realm" config="instance.config" properties="providerFactory.properties"></kc-component-config>
|
<kc-component-config realm="realm" config="instance.config" properties="providerFactory.properties"></kc-component-config>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageRealm">
|
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageRealm">
|
||||||
<button kc-save>{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
<button kc-cancel data-ng-disabled="!changed" data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="row" data-ng-show="access.manageUsers">
|
<div class="row" data-ng-show="access.manageUsers">
|
||||||
<div class="col-sm-4 col-sm-offset-4">
|
<div class="col-sm-4 col-sm-offset-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<select class="selectpicker form-control" ng-model="selectedProvider"
|
<select class="form-control" ng-model="selectedProvider"
|
||||||
ng-options="p.id for p in providers"
|
ng-options="p.id for p in providers"
|
||||||
data-ng-change="addProvider(selectedProvider); selectedProvider = null">
|
data-ng-change="addProvider(selectedProvider); selectedProvider = null">
|
||||||
<option value="" disabled selected>{{:: 'add-provider.placeholder' | translate}}</option>
|
<option value="" disabled selected>{{:: 'add-provider.placeholder' | translate}}</option>
|
||||||
|
|
|
@ -2,26 +2,26 @@
|
||||||
<div data-ng-repeat="option in properties" class="form-group" data-ng-controller="ProviderConfigCtrl">
|
<div data-ng-repeat="option in properties" class="form-group" data-ng-controller="ProviderConfigCtrl">
|
||||||
<label class="col-md-2 control-label">{{:: option.label | translate}}</label>
|
<label class="col-md-2 control-label">{{:: option.label | translate}}</label>
|
||||||
|
|
||||||
<div class="col-md-6" data-ng-show="option.type == 'String'">
|
<div class="col-md-6" data-ng-if="option.type == 'String'">
|
||||||
<input class="form-control" type="text" data-ng-model="config[ option.name ][0]" >
|
<input class="form-control" type="text" data-ng-model="config[ option.name ][0]" >
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" data-ng-show="option.type == 'Password'">
|
<div class="col-md-6" data-ng-if="option.type == 'Password'">
|
||||||
<input class="form-control" type="password" data-ng-model="config[ option.name ][0]" >
|
<input class="form-control" type="password" data-ng-model="config[ option.name ][0]" >
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" data-ng-show="option.type == 'boolean'">
|
<div class="col-md-6" data-ng-if="option.type == 'boolean'">
|
||||||
<input ng-model="config[ option.name ][0]" value="'true'" id="option.name" name="option.name" onoffswitchstring on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
|
<input ng-model="config[ option.name ][0]" value="'true'" id="option.name" name="option.name" onoffswitchstring on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" data-ng-show="option.type == 'List'">
|
<div class="col-md-6" data-ng-if="option.type == 'List'">
|
||||||
<select ng-model="config[ option.name ][0]" ng-options="data for data in option.options">
|
<select ng-model="config[ option.name ][0]" ng-options="data for data in option.options">
|
||||||
<option value="" selected> {{:: 'selectOne' | translate}} </option>
|
<option value="" selected> {{:: 'selectOne' | translate}} </option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" data-ng-show="option.type == 'MultivaluedList'">
|
<div class="col-md-6" data-ng-if="option.type == 'MultivaluedList'">
|
||||||
<select ui-select2 data-ng-model="config[ option.name ]" data-placeholder="{{:: 'selectMultiple' | translate}}..." multiple>
|
<select ui-select2 data-ng-model="config[ option.name ]" data-placeholder="{{:: 'selectMultiple' | translate}}..." multiple>
|
||||||
<option ng-repeat="val in option.options" value="{{val}}" ng-selected="true">{{val}}</option>
|
<option ng-repeat="val in option.options" value="{{val}}" ng-selected="true">{{val}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" data-ng-show="option.type == 'Role'">
|
<div class="col-md-6" data-ng-if="option.type == 'Role'">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<input class="form-control" type="text" data-ng-model="config[ option.name ][0]" >
|
<input class="form-control" type="text" data-ng-model="config[ option.name ][0]" >
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4" data-ng-show="option.type == 'ClientList'">
|
<div class="col-md-4" data-ng-if="option.type == 'ClientList'">
|
||||||
<select ng-model="config[ option.name ][0]" ng-options="client.clientId as client.clientId for client in clients">
|
<select ng-model="config[ option.name ][0]" ng-options="client.clientId as client.clientId for client in clients">
|
||||||
<option value="" selected> {{:: 'selectOne' | translate}} </option>
|
<option value="" selected> {{:: 'selectOne' | translate}} </option>
|
||||||
</select>
|
</select>
|
||||||
|
|
|
@ -22,8 +22,10 @@
|
||||||
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2]
|
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2]
|
||||||
|| path[2] == 'required-credentials'
|
|| path[2] == 'required-credentials'
|
||||||
|| path[2] == 'login-settings'
|
|| path[2] == 'login-settings'
|
||||||
|
|| path[2] == 'keys'
|
||||||
|| path[2] == 'theme-settings'
|
|| path[2] == 'theme-settings'
|
||||||
|| path[2] == 'token-settings'
|
|| path[2] == 'token-settings'
|
||||||
|
|| path[2] == 'client-registration'
|
||||||
|| path[2] == 'cache-settings'
|
|| path[2] == 'cache-settings'
|
||||||
|| path[2] == 'client-initial-access'
|
|| path[2] == 'client-initial-access'
|
||||||
|| path[2] == 'defense'
|
|| path[2] == 'defense'
|
||||||
|
@ -33,8 +35,13 @@
|
||||||
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'clients' || path[1] == 'client' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
|
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'clients' || path[1] == 'client' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
|
||||||
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'client-templates' || path[1] == 'client-template' || path[3] == 'client-templates') && 'active'"><a href="#/realms/{{realm.realm}}/client-templates"><i class="fa fa-cubes"></i> {{:: 'client-templates' | translate}}</a></li>
|
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'client-templates' || path[1] == 'client-template' || path[3] == 'client-templates') && 'active'"><a href="#/realms/{{realm.realm}}/client-templates"><i class="fa fa-cubes"></i> {{:: 'client-templates' | translate}}</a></li>
|
||||||
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
|
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
|
||||||
<li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
|
<li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings'
|
||||||
<li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation"><i class="fa fa-database"></i> {{:: 'user-federation' | translate}}</a></li>
|
|| path[2] == 'identity-provider-mappers'
|
||||||
|
|| path[1] == 'identity-provider-mappers'
|
||||||
|
|| path[1] == 'identity-provider') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
|
||||||
|
<li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-federation'
|
||||||
|
|| path[2] == 'user-federation'
|
||||||
|
|| path[1] == 'user-federation-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation"><i class="fa fa-database"></i> {{:: 'user-federation' | translate}}</a></li>
|
||||||
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication/flows"><i class="fa fa-lock"></i> {{:: 'authentication' | translate}}</a></li>
|
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication/flows"><i class="fa fa-lock"></i> {{:: 'authentication' | translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,10 +49,16 @@
|
||||||
<div class="nav-category" data-ng-show="current.realm">
|
<div class="nav-category" data-ng-show="current.realm">
|
||||||
<h2>{{:: 'manage' | translate}}</h2>
|
<h2>{{:: 'manage' | translate}}</h2>
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'groups' || path[1] == 'group') && 'active'"><a href="#/realms/{{realm.realm}}/groups"><span class="pficon pficon-users"></span> {{:: 'groups' | translate}}</a></li>
|
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'groups'
|
||||||
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> {{:: 'users' | translate}}</a></li>
|
|| path[1] == 'group'
|
||||||
|
|| path[2] == 'default-groups') && 'active'"><a href="#/realms/{{realm.realm}}/groups"><span class="pficon pficon-users"></span> {{:: 'groups' | translate}}</a></li>
|
||||||
|
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users'
|
||||||
|
|| path[1] == 'user'
|
||||||
|
|| path[1] == 'federated-identity') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> {{:: 'users' | translate}}</a></li>
|
||||||
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/realm"><i class="fa fa-clock-o"></i> {{:: 'sessions' | translate}}</a></li>
|
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/realm"><i class="fa fa-clock-o"></i> {{:: 'sessions' | translate}}</a></li>
|
||||||
<li data-ng-show="access.viewEvents" data-ng-class="(path[2] == 'events' || path[2] == 'events-settings') && 'active'"><a href="#/realms/{{realm.realm}}/events"><i class="fa fa-calendar"></i> {{:: 'events' | translate}}</a></li>
|
<li data-ng-show="access.viewEvents" data-ng-class="(path[2] == 'events'
|
||||||
|
|| path[2] == 'events-settings'
|
||||||
|
|| path[2] == 'admin-events') && 'active'"><a href="#/realms/{{realm.realm}}/events"><i class="fa fa-calendar"></i> {{:: 'events' | translate}}</a></li>
|
||||||
<li data-ng-show="access.manageRealm" ng-class="(path[2] =='partial-import') && 'active'"><a href="#/realms/{{realm.realm}}/partial-import"><span class="pficon pficon-import"></span> {{:: 'import' | translate}}</a></li>
|
<li data-ng-show="access.manageRealm" ng-class="(path[2] =='partial-import') && 'active'"><a href="#/realms/{{realm.realm}}/partial-import"><span class="pficon pficon-import"></span> {{:: 'import' | translate}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,41 +5,28 @@
|
||||||
<#elseif section = "header">
|
<#elseif section = "header">
|
||||||
${msg("loginTotpTitle")}
|
${msg("loginTotpTitle")}
|
||||||
<#elseif section = "form">
|
<#elseif section = "form">
|
||||||
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
|
<ol id="kc-totp-settings">
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<li>
|
||||||
<div class="${properties.kcLabelWrapperClass!}">
|
<p>${msg("loginTotpStep1")}</p>
|
||||||
<label for="otp" class="${properties.kcLabelClass!}">${msg("loginTotpOneTime")}</label>
|
</li>
|
||||||
</div>
|
<li>
|
||||||
<div class="${properties.kcInputWrapperClass!}">
|
<p>${msg("loginTotpStep2")}</p>
|
||||||
<input type="text" id="totp" name="totp" autocomplete="off" class="${properties.kcInputClass!}" />
|
<img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
|
||||||
</div>
|
<span class="code">${totp.totpSecretEncoded}</span>
|
||||||
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>${msg("loginTotpStep3")}</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
|
||||||
|
<div class="${properties.kcFormGroupClass!}">
|
||||||
|
<div class="${properties.kcInputWrapperClass!}">
|
||||||
|
<input type="text" id="totp" name="totp" autocomplete="off" class="${properties.kcInputClass!}" />
|
||||||
</div>
|
</div>
|
||||||
|
<input type="hidden" id="totpSecret" name="totpSecret" value="${totp.totpSecret}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="${properties.kcFormGroupClass!}">
|
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
|
||||||
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
|
</form>
|
||||||
<div class="${properties.kcFormOptionsWrapperClass!}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
|
|
||||||
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" type="submit" value="${msg("doSubmit")}"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<#elseif section = "info" >
|
|
||||||
<ol id="kc-totp-settings">
|
|
||||||
<li>
|
|
||||||
<p>${msg("loginTotpStep1")}</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>${msg("loginTotpStep2")}</p>
|
|
||||||
<img src="data:image/png;base64, ${totp.totpSecretQrCode}" alt="Figure: Barcode"><br/>
|
|
||||||
<span class="code">${totp.totpSecretEncoded}</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<p>${msg("loginTotpStep3")}</p>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</#if>
|
</#if>
|
||||||
</@layout.registrationLayout>
|
</@layout.registrationLayout>
|
||||||
|
|
|
@ -208,18 +208,6 @@ ol li img {
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol li span {
|
|
||||||
padding: 15px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
top: 46px;
|
|
||||||
left: 270px;
|
|
||||||
right: 50px;
|
|
||||||
position: absolute;
|
|
||||||
font-family: courier, monospace;
|
|
||||||
font-size: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr + .form-horizontal {
|
hr + .form-horizontal {
|
||||||
border: none;
|
border: none;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
/* FileSaver.js
|
/* FileSaver.js
|
||||||
* A saveAs() FileSaver implementation.
|
* A saveAs() FileSaver implementation.
|
||||||
* 2014-05-27
|
* 1.3.2
|
||||||
|
* 2016-06-16 18:25:19
|
||||||
*
|
*
|
||||||
* By Eli Grey, http://eligrey.com
|
* By Eli Grey, http://eligrey.com
|
||||||
* License: X11/MIT
|
* License: MIT
|
||||||
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*global self */
|
/*global self */
|
||||||
|
@ -12,16 +13,10 @@
|
||||||
|
|
||||||
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
||||||
|
|
||||||
var saveAs = saveAs
|
var saveAs = saveAs || (function(view) {
|
||||||
// IE 10+ (native saveAs)
|
|
||||||
|| (typeof navigator !== "undefined" &&
|
|
||||||
navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator))
|
|
||||||
// Everyone else
|
|
||||||
|| (function(view) {
|
|
||||||
"use strict";
|
"use strict";
|
||||||
// IE <10 is explicitly unsupported
|
// IE <10 is explicitly unsupported
|
||||||
if (typeof navigator !== "undefined" &&
|
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
|
||||||
/MSIE [1-9]\./.test(navigator.userAgent)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var
|
var
|
||||||
|
@ -31,36 +26,30 @@ var saveAs = saveAs
|
||||||
return view.URL || view.webkitURL || view;
|
return view.URL || view.webkitURL || view;
|
||||||
}
|
}
|
||||||
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
||||||
, can_use_save_link = !view.externalHost && "download" in save_link
|
, can_use_save_link = "download" in save_link
|
||||||
, click = function(node) {
|
, click = function(node) {
|
||||||
var event = doc.createEvent("MouseEvents");
|
var event = new MouseEvent("click");
|
||||||
event.initMouseEvent(
|
|
||||||
"click", true, false, view, 0, 0, 0, 0, 0
|
|
||||||
, false, false, false, false, 0, null
|
|
||||||
);
|
|
||||||
node.dispatchEvent(event);
|
node.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
, webkit_req_fs = view.webkitRequestFileSystem
|
, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
|
||||||
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
|
, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
|
||||||
, throw_outside = function(ex) {
|
, throw_outside = function(ex) {
|
||||||
(view.setImmediate || view.setTimeout)(function() {
|
(view.setImmediate || view.setTimeout)(function() {
|
||||||
throw ex;
|
throw ex;
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
, force_saveable_type = "application/octet-stream"
|
, force_saveable_type = "application/octet-stream"
|
||||||
, fs_min_size = 0
|
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
|
||||||
, deletion_queue = []
|
, arbitrary_revoke_timeout = 1000 * 40 // in ms
|
||||||
, process_deletion_queue = function() {
|
, revoke = function(file) {
|
||||||
var i = deletion_queue.length;
|
var revoker = function() {
|
||||||
while (i--) {
|
|
||||||
var file = deletion_queue[i];
|
|
||||||
if (typeof file === "string") { // file is an object URL
|
if (typeof file === "string") { // file is an object URL
|
||||||
get_URL().revokeObjectURL(file);
|
get_URL().revokeObjectURL(file);
|
||||||
} else { // file is a File
|
} else { // file is a File
|
||||||
file.remove();
|
file.remove();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
deletion_queue.length = 0; // clear queue
|
setTimeout(revoker, arbitrary_revoke_timeout);
|
||||||
}
|
}
|
||||||
, dispatch = function(filesaver, event_types, event) {
|
, dispatch = function(filesaver, event_types, event) {
|
||||||
event_types = [].concat(event_types);
|
event_types = [].concat(event_types);
|
||||||
|
@ -76,134 +65,97 @@ var saveAs = saveAs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
, FileSaver = function(blob, name) {
|
, auto_bom = function(blob) {
|
||||||
|
// prepend BOM for UTF-8 XML and text/* types (including HTML)
|
||||||
|
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
|
||||||
|
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
|
||||||
|
return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
|
||||||
|
}
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
, FileSaver = function(blob, name, no_auto_bom) {
|
||||||
|
if (!no_auto_bom) {
|
||||||
|
blob = auto_bom(blob);
|
||||||
|
}
|
||||||
// First try a.download, then web filesystem, then object URLs
|
// First try a.download, then web filesystem, then object URLs
|
||||||
var
|
var
|
||||||
filesaver = this
|
filesaver = this
|
||||||
, type = blob.type
|
, type = blob.type
|
||||||
, blob_changed = false
|
, force = type === force_saveable_type
|
||||||
, object_url
|
, object_url
|
||||||
, target_view
|
|
||||||
, get_object_url = function() {
|
|
||||||
var object_url = get_URL().createObjectURL(blob);
|
|
||||||
deletion_queue.push(object_url);
|
|
||||||
return object_url;
|
|
||||||
}
|
|
||||||
, dispatch_all = function() {
|
, dispatch_all = function() {
|
||||||
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
||||||
}
|
}
|
||||||
// on any filesys errors revert to saving with object URLs
|
// on any filesys errors revert to saving with object URLs
|
||||||
, fs_error = function() {
|
, fs_error = function() {
|
||||||
// don't create more object URLs than needed
|
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
|
||||||
if (blob_changed || !object_url) {
|
// Safari doesn't allow downloading of blob urls
|
||||||
object_url = get_object_url(blob);
|
var reader = new FileReader();
|
||||||
|
reader.onloadend = function() {
|
||||||
|
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
|
||||||
|
var popup = view.open(url, '_blank');
|
||||||
|
if(!popup) view.location.href = url;
|
||||||
|
url=undefined; // release reference before dispatching
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
dispatch_all();
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
filesaver.readyState = filesaver.INIT;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (target_view) {
|
// don't create more object URLs than needed
|
||||||
target_view.location.href = object_url;
|
if (!object_url) {
|
||||||
|
object_url = get_URL().createObjectURL(blob);
|
||||||
|
}
|
||||||
|
if (force) {
|
||||||
|
view.location.href = object_url;
|
||||||
} else {
|
} else {
|
||||||
window.open(object_url, "_blank");
|
var opened = view.open(object_url, "_blank");
|
||||||
|
if (!opened) {
|
||||||
|
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
|
||||||
|
view.location.href = object_url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
filesaver.readyState = filesaver.DONE;
|
filesaver.readyState = filesaver.DONE;
|
||||||
dispatch_all();
|
dispatch_all();
|
||||||
|
revoke(object_url);
|
||||||
}
|
}
|
||||||
, abortable = function(func) {
|
|
||||||
return function() {
|
|
||||||
if (filesaver.readyState !== filesaver.DONE) {
|
|
||||||
return func.apply(this, arguments);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
, create_if_not_found = {create: true, exclusive: false}
|
|
||||||
, slice
|
|
||||||
;
|
;
|
||||||
filesaver.readyState = filesaver.INIT;
|
filesaver.readyState = filesaver.INIT;
|
||||||
if (!name) {
|
|
||||||
name = "download";
|
|
||||||
}
|
|
||||||
if (can_use_save_link) {
|
if (can_use_save_link) {
|
||||||
object_url = get_object_url(blob);
|
object_url = get_URL().createObjectURL(blob);
|
||||||
save_link.href = object_url;
|
setTimeout(function() {
|
||||||
save_link.download = name;
|
save_link.href = object_url;
|
||||||
click(save_link);
|
save_link.download = name;
|
||||||
filesaver.readyState = filesaver.DONE;
|
click(save_link);
|
||||||
dispatch_all();
|
dispatch_all();
|
||||||
|
revoke(object_url);
|
||||||
|
filesaver.readyState = filesaver.DONE;
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
|
||||||
// viewed in a tab, so I force save with application/octet-stream
|
fs_error();
|
||||||
// http://code.google.com/p/chromium/issues/detail?id=91158
|
|
||||||
if (view.chrome && type && type !== force_saveable_type) {
|
|
||||||
slice = blob.slice || blob.webkitSlice;
|
|
||||||
blob = slice.call(blob, 0, blob.size, force_saveable_type);
|
|
||||||
blob_changed = true;
|
|
||||||
}
|
|
||||||
// Since I can't be sure that the guessed media type will trigger a download
|
|
||||||
// in WebKit, I append .download to the filename.
|
|
||||||
// https://bugs.webkit.org/show_bug.cgi?id=65440
|
|
||||||
if (webkit_req_fs && name !== "download") {
|
|
||||||
name += ".download";
|
|
||||||
}
|
|
||||||
if (type === force_saveable_type || webkit_req_fs) {
|
|
||||||
target_view = view;
|
|
||||||
}
|
|
||||||
if (!req_fs) {
|
|
||||||
fs_error();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fs_min_size += blob.size;
|
|
||||||
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
|
|
||||||
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
|
|
||||||
var save = function() {
|
|
||||||
dir.getFile(name, create_if_not_found, abortable(function(file) {
|
|
||||||
file.createWriter(abortable(function(writer) {
|
|
||||||
writer.onwriteend = function(event) {
|
|
||||||
target_view.location.href = file.toURL();
|
|
||||||
deletion_queue.push(file);
|
|
||||||
filesaver.readyState = filesaver.DONE;
|
|
||||||
dispatch(filesaver, "writeend", event);
|
|
||||||
};
|
|
||||||
writer.onerror = function() {
|
|
||||||
var error = writer.error;
|
|
||||||
if (error.code !== error.ABORT_ERR) {
|
|
||||||
fs_error();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
"writestart progress write abort".split(" ").forEach(function(event) {
|
|
||||||
writer["on" + event] = filesaver["on" + event];
|
|
||||||
});
|
|
||||||
writer.write(blob);
|
|
||||||
filesaver.abort = function() {
|
|
||||||
writer.abort();
|
|
||||||
filesaver.readyState = filesaver.DONE;
|
|
||||||
};
|
|
||||||
filesaver.readyState = filesaver.WRITING;
|
|
||||||
}), fs_error);
|
|
||||||
}), fs_error);
|
|
||||||
};
|
|
||||||
dir.getFile(name, {create: false}, abortable(function(file) {
|
|
||||||
// delete file if it already exists
|
|
||||||
file.remove();
|
|
||||||
save();
|
|
||||||
}), abortable(function(ex) {
|
|
||||||
if (ex.code === ex.NOT_FOUND_ERR) {
|
|
||||||
save();
|
|
||||||
} else {
|
|
||||||
fs_error();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}), fs_error);
|
|
||||||
}), fs_error);
|
|
||||||
}
|
}
|
||||||
, FS_proto = FileSaver.prototype
|
, FS_proto = FileSaver.prototype
|
||||||
, saveAs = function(blob, name) {
|
, saveAs = function(blob, name, no_auto_bom) {
|
||||||
return new FileSaver(blob, name);
|
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
FS_proto.abort = function() {
|
// IE 10+ (native saveAs)
|
||||||
var filesaver = this;
|
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
|
||||||
filesaver.readyState = filesaver.DONE;
|
return function(blob, name, no_auto_bom) {
|
||||||
dispatch(filesaver, "abort");
|
name = name || blob.name || "download";
|
||||||
};
|
|
||||||
|
if (!no_auto_bom) {
|
||||||
|
blob = auto_bom(blob);
|
||||||
|
}
|
||||||
|
return navigator.msSaveOrOpenBlob(blob, name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
FS_proto.abort = function(){};
|
||||||
FS_proto.readyState = FS_proto.INIT = 0;
|
FS_proto.readyState = FS_proto.INIT = 0;
|
||||||
FS_proto.WRITING = 1;
|
FS_proto.WRITING = 1;
|
||||||
FS_proto.DONE = 2;
|
FS_proto.DONE = 2;
|
||||||
|
@ -217,11 +169,6 @@ var saveAs = saveAs
|
||||||
FS_proto.onwriteend =
|
FS_proto.onwriteend =
|
||||||
null;
|
null;
|
||||||
|
|
||||||
view.addEventListener("unload", process_deletion_queue, false);
|
|
||||||
saveAs.unload = function() {
|
|
||||||
process_deletion_queue();
|
|
||||||
view.removeEventListener("unload", process_deletion_queue, false);
|
|
||||||
};
|
|
||||||
return saveAs;
|
return saveAs;
|
||||||
}(
|
}(
|
||||||
typeof self !== "undefined" && self
|
typeof self !== "undefined" && self
|
||||||
|
@ -232,10 +179,10 @@ var saveAs = saveAs
|
||||||
// while `this` is nsIContentFrameMessageManager
|
// while `this` is nsIContentFrameMessageManager
|
||||||
// with an attribute `content` that corresponds to the window
|
// with an attribute `content` that corresponds to the window
|
||||||
|
|
||||||
if (typeof module !== "undefined" && module !== null) {
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
module.exports = saveAs;
|
module.exports.saveAs = saveAs;
|
||||||
} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) {
|
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
|
||||||
define([], function() {
|
define("FileSaver.js", function() {
|
||||||
return saveAs;
|
return saveAs;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue