diff options
author | Yuuta Liang <yuutaw@student.cs.ubc.ca> | 2023-11-28 18:19:39 -0800 |
---|---|---|
committer | Yuuta Liang <yuutaw@student.cs.ubc.ca> | 2023-11-28 18:19:39 -0800 |
commit | 1073af21305360bd33903c533cdac57e9f936294 (patch) | |
tree | 2c2d9c343ffe2577286fb53e016f06f6cdc53cbf /src/main/ui/tui | |
parent | e13adbb9a9146dd5ece890449e3cad958a502f86 (diff) | |
download | jca-1073af21305360bd33903c533cdac57e9f936294.tar jca-1073af21305360bd33903c533cdac57e9f936294.tar.gz jca-1073af21305360bd33903c533cdac57e9f936294.tar.bz2 jca-1073af21305360bd33903c533cdac57e9f936294.zip |
Move TUI and GUI into separate packages
Signed-off-by: Yuuta Liang <yuutaw@student.cs.ubc.ca>
Diffstat (limited to 'src/main/ui/tui')
-rw-r--r-- | src/main/ui/tui/IssueScreen.java | 168 | ||||
-rw-r--r-- | src/main/ui/tui/JCA.java | 220 | ||||
-rw-r--r-- | src/main/ui/tui/MainScreen.java | 234 | ||||
-rw-r--r-- | src/main/ui/tui/MgmtScreen.java | 153 | ||||
-rw-r--r-- | src/main/ui/tui/Screen.java | 31 | ||||
-rw-r--r-- | src/main/ui/tui/TemplateSetScreen.java | 157 | ||||
-rw-r--r-- | src/main/ui/tui/TemplatesScreen.java | 138 | ||||
-rw-r--r-- | src/main/ui/tui/UIHandler.java | 45 |
8 files changed, 1146 insertions, 0 deletions
diff --git a/src/main/ui/tui/IssueScreen.java b/src/main/ui/tui/IssueScreen.java new file mode 100644 index 0000000..26ceb02 --- /dev/null +++ b/src/main/ui/tui/IssueScreen.java @@ -0,0 +1,168 @@ +package ui.tui; + +import model.asn1.exceptions.ParseException; +import model.ca.Template; +import model.csr.CertificationRequest; +import model.pki.cert.Certificate; +import model.x501.Name; +import ui.Utils; + +/** + * The screen that accepts a CSR and template and allows user to change its properties and issue. + */ +public class IssueScreen implements UIHandler { + private final JCA session; + + private Template template; + private CertificationRequest incomingCSR; + + /** + * EFFECTS: Init with the session. + */ + public IssueScreen(JCA session) { + this.session = session; + } + + /** + * EFFECTS: Set current template and CSR in use by args. + * REQUIRES: args.length = 2, args[0] instanceof CertificateRequest, args[1] instanceof Template + * MODIFIES: args[1] + */ + @Override + public void enter(Object... args) { + this.incomingCSR = (CertificationRequest) args[0]; + this.template = (Template) args[1]; + } + + /** + * EFFECTS: Print help. + */ + @Override + public void help() { + System.out.print("show\tView the current certificate\n" + + "set\tSet properties or template\n" + + "commit\tIssue the certificate\n" + + "exit\tDiscard and go to main menu\n" + + "help\tPrint this message\n"); + } + + /** + * EFFECTS: Print pending cert info. + */ + @Override + public void show() { + System.out.println("Requested Subject:\t" + incomingCSR.getCertificationRequestInfo().getSubject()); + System.out.println("Subject:\t" + (template.getSubject() == null + ? incomingCSR.getCertificationRequestInfo().getSubject() + : template.getSubject())); + System.out.println("Template:\t" + template.getName()); + System.out.println("Validity:\t" + template.getValidity() + " days"); + } + + /** + * EFFECTS: Issue the cert and log it. + * MODIFIES: session + */ + @Override + public void commit() { + try { + Certificate certificate = session.getCa().signCert(incomingCSR.getCertificationRequestInfo(), template); + session.save(); + System.out.println(Utils.toPEM(certificate.encodeDER(), "CERTIFICATE")); + session.setScreen(Screen.MAIN); + } catch (Throwable e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Set or unset the subject. + * MODIFIES: this + */ + private void handleIssueSetSubject(String val) { + try { + if (val == null) { + template = new Template(template.getName(), template.isEnabled(), (Name) null, template.getValidity()); + } else { + template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); + } + } catch (ParseException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Set or unset the validity. + * MODIFIES: this + */ + private void handleIssueSetValidity(String val) { + if (val == null) { + System.out.println("Cannot unset validity"); + return; + } + try { + long i = Long.parseLong(val); + if (i <= 0) { + System.out.println("Invalid validity days"); + return; + } + template = new Template(template.getName(), template.isEnabled(), template.getSubject(), i); + } catch (NumberFormatException ignored) { + System.out.println("Invalid validity days"); + } + } + + /** + * EFFECTS: Handle the set command. + * MODIFIES: this + */ + private void handleIssueSet(String... args) { + if (args.length != 2 && args.length != 3) { + System.out.println("Usage: set <key> <value>"); + System.out.println("Supported keys: subject validity"); + return; + } + String val = args.length == 3 ? args[2] : null; + switch (args[1]) { + case "subject": + handleIssueSetSubject(val); + break; + case "validity": + handleIssueSetValidity(val); + break; + default: + System.out.println("Unknown key"); + break; + } + } + + @Override + public void command(String... args) { + if (args[0].equals("set")) { + handleIssueSet(args); + } else { + help(); + } + } + + /** + * EFFECTS: Clear the certificates and return main. + * MODIFIES: this + */ + @Override + public Screen exit() { + incomingCSR = null; + template = null; + return Screen.MAIN; + } + + /** + * EFFECTS: Return "/subj/ %" + */ + @Override + public String getPS1() { + return String.format("/%s/ %%", template.getSubject() == null + ? incomingCSR.getCertificationRequestInfo().getSubject() + : template.getSubject()); + } +} diff --git a/src/main/ui/tui/JCA.java b/src/main/ui/tui/JCA.java new file mode 100644 index 0000000..c23d610 --- /dev/null +++ b/src/main/ui/tui/JCA.java @@ -0,0 +1,220 @@ +package ui.tui; + +import model.asn1.exceptions.InvalidDBException; +import model.asn1.exceptions.ParseException; +import model.ca.CertificationAuthority; +import persistence.Decoder; +import persistence.FS; +import ui.Utils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.util.Scanner; + +/** + * Main program + */ +public class JCA { + /** + * Default db file (./data/ca.json) + */ + private static final Path PATH_DEFAULT = Path.of("data", "ca.json"); + + /** + * Instances of the five screens; + */ + private final UIHandler mainScreen; + private final UIHandler mgmtScreen; + private final UIHandler issueScreen; + private final UIHandler templatesScreen; + private final UIHandler templateSetScreen; + /** + * The CA + */ + private CertificationAuthority ca; + /** + * The current screen. + */ + private UIHandler screen; + + /** + * There are unsaved changes. + */ + private boolean unsaved = false; + + /** + * EFFECTS: Init with main screen and empty CA. No private key and no CA cert. + * Throws {@link NoSuchAlgorithmException} when crypto issue happens. + */ + public JCA() throws NoSuchAlgorithmException, InvalidKeySpecException { + this.mainScreen = new MainScreen(this); + this.mgmtScreen = new MgmtScreen(this); + this.issueScreen = new IssueScreen(this); + this.templatesScreen = new TemplatesScreen(this); + this.templateSetScreen = new TemplateSetScreen(this); + + setScreen(Screen.MAIN); + + this.ca = new CertificationAuthority(); + } + + /** + * EFFECT: Checks if the CA is installed or not (according to the desired state) and print if not matching. Returns + * true if matching. + */ + public boolean checkCA(boolean requireInstalled) { + if (requireInstalled && ca.getCertificate() == null) { + System.out.println("No CA installed"); + return false; + } else if (!requireInstalled && ca.getCertificate() != null) { + System.out.println("CA already installed"); + return false; + } + return true; + } + + /** + * EFFECTS: Read PEM from stdin, matched the given tag. + * Throws {@link ParseException} if the input is incorrect. + */ + public Byte[] handleInputPEM(String desiredTag) throws ParseException { + final Scanner scanner = new Scanner(System.in); + StringBuilder in = new StringBuilder(); + while (true) { + final String line = scanner.nextLine(); + in.append(line); + in.append("\n"); + if (line.matches("-----END .*-----")) { + break; + } + } + return Utils.parsePEM(Utils.byteToByte(in.toString().getBytes(StandardCharsets.UTF_8)), desiredTag); + } + + /** + * EFFECT: Set the current screen with optional args. Exit the program when mode is null. + * MODIFIES: this + */ + public void setScreen(Screen mode, Object... args) { + if (mode == null) { + System.exit(0); + } + switch (mode) { + case MAIN: + this.screen = mainScreen; + break; + case MGMT: + this.screen = mgmtScreen; + break; + case ISSUE: + this.screen = issueScreen; + break; + case TEMPLATES: + this.screen = templatesScreen; + break; + case TEMPLATE_SET: + this.screen = templateSetScreen; + break; + } + screen.enter(args); + } + + /** + * EFFECTS: Read the database file and replace all local states. + * MODIFIES: this + */ + private void load() { + if (unsaved) { + System.out.println("Current database is not saved yet."); + return; + } + try { + this.ca = Decoder.decodeCA(FS.read(PATH_DEFAULT)); + } catch (InvalidDBException | NoSuchAlgorithmException e) { + System.out.println(e.getMessage()); + if (e.getCause() != null) { + System.out.println(e.getCause().getMessage()); + e.getCause().printStackTrace(); + } + } + } + + /** + * EFFECTS: Handle input line + * MODIFIES: this + */ + private void handleLine(String... args) { + if (args[0].equals("log")) { + ca.getLogs().forEach(System.out::println); + return; + } + if ("help".equals(args[0])) { + screen.help(); + System.out.println("log\tView audit logs"); + System.out.println("load\tLoad database"); + System.out.println("save\tSave database"); + } else if ("commit".equals(args[0])) { + screen.commit(); + } else if ("show".equals(args[0])) { + screen.show(); + } else if ("exit".equals(args[0])) { + setScreen(screen.exit()); + } else if ("save".equals(args[0])) { + save(); + } else if ("load".equals(args[0])) { + load(); + } else { + screen.command(args); + } + printPS1(); + } + + /** + * EFFECTS: Print the '*user@JCA PS1' line + */ + private void printPS1() { + System.out.printf("%s%s@JCA %s ", + unsaved ? "*" : "", + ca.getUser(), + screen.getPS1()); + } + + /** + * EFFECTS: Save the DB + */ + public void save() { + try { + FS.write(PATH_DEFAULT, Decoder.encodeCA(ca)); + unsaved = false; + } catch (IOException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Run the program + */ + public void run() { + printPS1(); + final Scanner scanner = new Scanner(System.in); + while (true) { + String[] args = scanner.nextLine().split(" "); + if (args.length <= 0 || args[0].isBlank()) { + printPS1(); + continue; + } + handleLine(args); + } + } + + public void setUnsaved(boolean unsaved) { + this.unsaved = unsaved; + } + + public CertificationAuthority getCa() { + return ca; + } +} diff --git a/src/main/ui/tui/MainScreen.java b/src/main/ui/tui/MainScreen.java new file mode 100644 index 0000000..860eff7 --- /dev/null +++ b/src/main/ui/tui/MainScreen.java @@ -0,0 +1,234 @@ +package ui.tui; + +import model.asn1.ASN1Object; +import model.asn1.UtcTime; +import model.asn1.exceptions.ParseException; +import model.asn1.parsing.BytesReader; +import model.ca.Template; +import model.csr.CertificationRequest; +import model.pki.cert.Certificate; +import model.pki.crl.CertificateList; +import model.pki.crl.Reason; +import model.pki.crl.RevokedCertificate; +import ui.Utils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Optional; + +/** + * The main screen that handles submenus (mgmt / issue / template), list certs, revoke certs, generate CRLs. + */ +public class MainScreen implements UIHandler { + private final JCA session; + + /** + * EFFECTS: Init with the parent session. + */ + public MainScreen(JCA session) { + this.session = session; + } + + /** + * EFFECTS: Print help + */ + @Override + public void help() { + System.out.print("mgmt\tView and manage the CA certificate\n" + + "issue\tIssue a certificate\n" + + "show\tList all issued certificates\n" + + "export\tExport a certificate to file (DER)\n" + + "template\tManage templates\n" + + "revoke\tRevoke a certificate\n" + + "crl\t\tSign CRL\n" + + "log\t\tView audit logs\n" + + "exit\tExit\n" + + "help\tPrint this message\n"); + } + + /** + * EFFECTS: Print each issued cert in Subject Serial Status format. + */ + @Override + public void show() { + session.getCa().getSigned().forEach(cert -> { + System.out.printf("%s\t%d\t%s\n", + cert.getCertificate().getSubject().toString(), + cert.getCertificate().getSerialNumber().getLong(), + session.getCa().getRevoked().stream().anyMatch(rev -> rev.getSerialNumber().getLong() + == cert.getCertificate().getSerialNumber().getLong()) ? "REVOKED" : "OK"); + }); + } + + /** + * EFFECTS: Read the input CSR. + */ + private CertificationRequest handleIssueInputCSR() { + try { + return new CertificationRequest(new BytesReader(session.handleInputPEM("CERTIFICATE REQUEST")), + false); + } catch (ParseException e) { + System.out.println(e.getMessage()); + return null; + } + } + + /** + * EFFECTS: Handle the issue command. Read CSR, find template, switch to issue screen. + */ + private void handleIssue(String... args) { + if (!session.checkCA(true)) { + return; + } + if (args.length <= 1) { + System.out.println("Usage: issue <template>"); + return; + } + Template tmp = session.getCa().findTemplate(args[1], true); + if (tmp == null) { + System.out.println("Cannot find the template specified"); + return; + } + CertificationRequest req = handleIssueInputCSR(); + if (req != null) { + session.setScreen(Screen.ISSUE, req, new Template(tmp.getName(), + true, + tmp.getSubject(), + tmp.getValidity())); + } + } + + /** + * EFFECTS: Find issued and not revoked certificate by serial. Return null if not found. + */ + private Certificate findCertBySerial(int serial) { + Optional<Certificate> c = session.getCa().getSigned() + .stream() + .filter(cert -> cert.getCertificate().getSerialNumber().getLong() == serial) + .findFirst(); + if (c.isEmpty()) { + System.out.println("Cannot find the certificate specified"); + return null; + } + if (session.getCa().getRevoked().stream().anyMatch(rev -> rev.getSerialNumber().getLong() == serial)) { + System.out.println("The certificate has already been revoked."); + return null; + } + return c.get(); + } + + /** + * EFFECTS: Handle the revoke command and log it. + * MODIFIES: session + */ + private void handleRevoke(String... args) { + if (args.length < 3) { + System.out.println("Usage: revoke <serial> <reason>"); + return; + } + try { + final Reason reason = Reason.valueOf(args[2]); + int serial = Integer.parseInt(args[1]); + Certificate c = findCertBySerial(serial); + if (c == null) { + return; + } + session.getCa().revoke(new RevokedCertificate(ASN1Object.TAG_SEQUENCE, null, + c.getCertificate().getSerialNumber(), + new UtcTime(UtcTime.TAG, null, ZonedDateTime.now(ZoneId.of("UTC"))), reason)); + session.save(); + } catch (IllegalArgumentException ignored) { + System.out.println("Illegal serial number or reason"); + } + } + + /** + * EFFECTS: Export a cert to file + */ + private void handleExport(String... args) { + if (args.length < 3) { + System.out.println("Usage: export <serial> <path>"); + return; + } + try { + int serial = Integer.parseInt(args[1]); + Certificate c = findCertBySerial(serial); + if (c == null) { + return; + } + final File fd = new File(args[2]); + final OutputStream out = new FileOutputStream(fd); + out.write(Utils.byteToByte(c.encodeDER())); + out.close(); + } catch (IllegalArgumentException ignored) { + System.out.println("Illegal serial number"); + } catch (IOException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Issue a CRL and do audit log. + * MODIFIES: session + */ + private void handleCRL() { + if (!session.checkCA(true)) { + return; + } + try { + CertificateList crl = session.getCa().signCRL(); + session.save(); + System.out.println(Utils.toPEM(crl.encodeDER(), "X509 CRL")); + } catch (Throwable e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Handle commands + */ + @Override + public void command(String... args) { + switch (args[0]) { + case "mgmt": + session.setScreen(Screen.MGMT); + return; + case "issue": + handleIssue(args); + return; + case "revoke": + handleRevoke(args); + return; + case "export": + handleExport(args); + return; + case "template": + session.setScreen(Screen.TEMPLATES); + return; + case "crl": + handleCRL(); + return; + } + help(); + } + + /** + * EFFECTS: Exit the program + */ + @Override + public Screen exit() { + return null; + } + + /** + * EFFECTS: return "/ %" + */ + @Override + public String getPS1() { + return "/ %"; + } +} diff --git a/src/main/ui/tui/MgmtScreen.java b/src/main/ui/tui/MgmtScreen.java new file mode 100644 index 0000000..007bd0e --- /dev/null +++ b/src/main/ui/tui/MgmtScreen.java @@ -0,0 +1,153 @@ +package ui.tui; + +import model.asn1.exceptions.InvalidCAException; +import model.asn1.exceptions.ParseException; +import model.asn1.parsing.BytesReader; +import model.csr.CertificationRequest; +import model.pki.cert.Certificate; +import model.pki.cert.TbsCertificate; +import ui.Utils; + +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +/** + * Manage the private key and CA certificate. It can print the public key, generate CSR, and install CA cert. + */ +public class MgmtScreen implements UIHandler { + private final JCA session; + + /** + * EFFECTS: Init with the parent session. + */ + public MgmtScreen(JCA session) { + this.session = session; + } + + /** + * EFFECTS: Print help + */ + @Override + public void help() { + System.out.print("show\tView the public key and CA certificate\n" + + "genkey\tGenerate a RSA private key\n" + + "csr\tGenerate a CSR for a upper-level CA to sign\n" + + "install\tInstall a CA certificate\n" + + "exit\tGo to main menu\n" + + "help\tPrint this message\n"); + } + + /** + * EFFECTS: Format the public key and CA + */ + @Override + public void show() { + if (session.getCa().getPublicKey() == null) { + System.out.println("No private key installed"); + } else { + System.out.println("Public Key (RSA2048):"); + System.out.printf("\tModules:\t\t%s\n", session.getCa().getPublicKey().getModulus().toString(10)); + System.out.printf("\tPublic Exponent:\t%s\n", + session.getCa().getPublicKey().getPublicExponent().toString(16)); + } + if (!session.checkCA(true)) { + return; + } + final TbsCertificate info = session.getCa().getCertificate().getCertificate(); + System.out.printf("Subject:\t%s\n", info.getSubject().toString()); + System.out.printf("Issuer:\t%s\n", info.getIssuer().toString()); + System.out.printf("Not Before:\t%s\n", info.getValidity().getNotBefore().getTimestamp()); + System.out.printf("Not After:\t%s\n", info.getValidity().getNotAfter().getTimestamp()); + System.out.printf("Signature:\t%s\n", + Base64.getEncoder().encodeToString(Utils.byteToByte(info.getSubjectPublicKeyInfo() + .getSubjectPublicKey().getConvertedVal()))); + } + + /** + * EFFECT: Generate a CSR + * MODIFIES: session + */ + private void handleCSR() { + if (!session.checkCA(false)) { + return; + } + try { + CertificationRequest req = session.getCa().signCSR(); + System.out.println(Utils.toPEM(req.encodeDER(), "CERTIFICATE REQUEST")); + session.setUnsaved(true); + } catch (Throwable e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Handle the 'install' command. Read incoming certificate and validate it. + * MODIFIES: session + */ + private void handleInstall() { + if (!session.checkCA(false)) { + return; + } + try { + final Byte[] in = session.handleInputPEM("CERTIFICATE"); + final Certificate cert = new Certificate(new BytesReader(in), false); + session.getCa().installCertificate(cert); + session.setUnsaved(true); + } catch (InvalidCAException | ParseException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Handle the 'genkey' command. Generate a RSA2048 private key. + * MODIFIES: session + */ + private void handleGenKey() { + if (session.getCa().getPublicKey() != null) { + System.out.println("A private key is already installed."); + } + try { + session.getCa().generateKey(); + session.setUnsaved(true); + } catch (NoSuchAlgorithmException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Handle commands. + */ + @Override + public void command(String... args) { + switch (args[0]) { + case "genkey": + handleGenKey(); + break; + case "csr": + handleCSR(); + break; + case "install": + handleInstall(); + break; + default: + help(); + break; + } + } + + /** + * EFFECTS: Go to main menu + */ + @Override + public Screen exit() { + return Screen.MAIN; + } + + /** + * EFFECTS: return "/ca/ #" + */ + @Override + public String getPS1() { + return "/ca/ #"; + } +}
\ No newline at end of file diff --git a/src/main/ui/tui/Screen.java b/src/main/ui/tui/Screen.java new file mode 100644 index 0000000..413fe8a --- /dev/null +++ b/src/main/ui/tui/Screen.java @@ -0,0 +1,31 @@ +package ui.tui; + +/** + * The screen type + */ +public enum Screen { + /** + * Main menu (mgmt, issue, template, crl, show, revoke, log) + */ + MAIN, + + /** + * The CA management menu (show, csr, install) + */ + MGMT, + + /** + * The issue menu (show, set, commit) + */ + ISSUE, + + /** + * The templates menu (show, add, enable, disable, remove) + */ + TEMPLATES, + + /** + * The template edit menu (show, set, commit) + */ + TEMPLATE_SET +} diff --git a/src/main/ui/tui/TemplateSetScreen.java b/src/main/ui/tui/TemplateSetScreen.java new file mode 100644 index 0000000..0f6df3a --- /dev/null +++ b/src/main/ui/tui/TemplateSetScreen.java @@ -0,0 +1,157 @@ +package ui.tui; + +import model.asn1.exceptions.ParseException; +import model.ca.Template; +import model.x501.Name; + +/** + * The screen that modifies the properties of a single template and add it to the store. + */ +public class TemplateSetScreen implements UIHandler { + private final JCA session; + + /** + * EFFECTS: Init with session. + */ + public TemplateSetScreen(JCA session) { + this.session = session; + } + + private Template template; + + /** + * EFFECTS: Print help + */ + @Override + public void help() { + System.out.println("show\tView the current template settings\n" + + "set\tSet key value\n" + + "commit\tSave the template\n" + + "exit\tDiscard changes\n" + + "help\tPrint this help message\n"); + } + + /** + * EFFECTS: Parse and set / unset the subject of the template + * MODIFIES: this + */ + private void handleSetSubject(String val) { + try { + if (val == null) { + template = new Template(template.getName(), template.isEnabled(), (Name) null, template.getValidity()); + } else { + template = new Template(template.getName(), template.isEnabled(), val, template.getValidity()); + } + } catch (ParseException e) { + System.out.println(e.getMessage()); + } + } + + /** + * EFFECTS: Set the validity of the template to the given integer + * MODIFIES: this + */ + private void handleSetValidity(String val) { + if (val == null) { + System.out.println("Cannot unset validity"); + return; + } + try { + long i = Long.parseLong(val); + if (i <= 0) { + System.out.println("Invalid validity days"); + return; + } + template = new Template(template.getName(), template.isEnabled(), template.getSubject(), i); + } catch (NumberFormatException ignored) { + System.out.println("Invalid validity days"); + } + } + + /** + * EFFECTS: Handle the `set` command. + * MODIFIES: this + */ + private void handleSet(String... args) { + if (args.length != 2 && args.length != 3) { + System.out.println("Usage: set <key> <value>"); + System.out.println("Supported keys: subject validity"); + return; + } + String val = args.length == 3 ? args[2] : null; + switch (args[1]) { + case "subject": + handleSetSubject(val); + break; + case "validity": + handleSetValidity(val); + break; + default: + System.out.println("Unknown key"); + break; + } + } + + /** + * EFFECTS: Add the template to store and switch to templates screen. + * MODIFIES: session + */ + @Override + public void commit() { + session.getCa().addTemplate(template); + session.setUnsaved(true); + session.setScreen(Screen.TEMPLATES); + } + + /** + * EFFECTS: Show template info. + */ + @Override + public void show() { + System.out.println("Subject:\t" + template.getSubject()); + System.out.println("Validity:\t" + template.getValidity() + " days"); + } + + /** + * EFFECTS: Handle commands + */ + @Override + public void command(String... args) { + switch (args[0]) { + case "set": + handleSet(args); + break; + default: + case "help": + help(); + break; + } + } + + /** + * EFFECTS: Return to templates list and clear the current template in editing. + */ + @Override + public Screen exit() { + template = null; + return Screen.TEMPLATES; + } + + /** + * EFFECTS: yuuta@JCA /templates/name/ % + */ + @Override + public String getPS1() { + return String.format("/templates/%s/ %%", template.getName()); + } + + /** + * EFFECT: Edit args[0]. + * REQUIRES: args.length = 1; args[0] instanceof Template + * MODIFIES: args[0] + */ + @Override + public void enter(Object... args) { + template = (Template) args[0]; + } +}
\ No newline at end of file diff --git a/src/main/ui/tui/TemplatesScreen.java b/src/main/ui/tui/TemplatesScreen.java new file mode 100644 index 0000000..6b7f18c --- /dev/null +++ b/src/main/ui/tui/TemplatesScreen.java @@ -0,0 +1,138 @@ +package ui.tui; + +import model.ca.Template; +import model.x501.Name; + +/** + * The screen that allows users to list templates and manage them. + */ +public class TemplatesScreen implements UIHandler { + private final JCA session; + + /** + * EFFECTS: Init with the session. + */ + public TemplatesScreen(JCA session) { + this.session = session; + } + + /** + * EFFECTS: Print help. + */ + @Override + public void help() { + System.out.println("show\tList templates\n" + + "add\tCreate a new template\n" + + "enable\tEnable a template\n" + + "disable\tDisable a template\n" + + "delete\tDelete a template\n" + + "exit\tGo to main menu\n" + + "help\tPrint this message"); + } + + /** + * EFFECTS: List templates in Name[ENABLED / DISABLED] Subject Validity format. + */ + @Override + public void show() { + session.getCa().getTemplates().forEach(tem -> + System.out.printf("%s[%s]\t%s\t%d Days\n", + tem.getName(), + tem.isEnabled() ? "ENABLED" : "DISABLED", + tem.getSubject(), + tem.getValidity())); + } + + /** + * EFFECTS: Create a new template with the given name and switch to the template set screen. + */ + private void handleAdd(String... args) { + if (args.length <= 1) { + System.out.println("Usage: add <name>"); + return; + } + if (session.getCa().findTemplate(args[1], false) != null) { + System.out.println("The template already exists."); + return; + } + + session.setScreen(Screen.TEMPLATE_SET, + new Template(args[1], false, (Name) null, 30)); + } + + /** + * EFFECTS: Handle the enable / disable commands. + * MODIFIES: session + */ + private void handleEnableDisable(boolean enable, String... args) { + if (args.length <= 1) { + System.out.printf("Usage: %s <template>\n", enable ? "enable" : "disable"); + return; + } + Template tmp = session.getCa().findTemplate(args[1], false); + if (tmp == null) { + System.out.println("Cannot find the template specified"); + return; + } + session.getCa().setTemplateEnable(tmp, enable); + session.setUnsaved(true); + } + + /** + * EFFECTS: Handle the delete command + * MODIFIES: session + */ + private void handleDelete(String... args) { + if (args.length <= 1) { + System.out.println("Usage: delete <template>"); + return; + } + Template tmp = session.getCa().findTemplate(args[1], true); + if (tmp == null) { + System.out.println("Cannot find the template specified"); + return; + } + session.getCa().removeTemplate(tmp); + session.setUnsaved(true); + } + + /** + * EFFECTS: Handle commands. + */ + @Override + public void command(String... args) { + switch (args[0]) { + case "add": + handleAdd(args); + break; + case "enable": + handleEnableDisable(true, args); + break; + case "disable": + handleEnableDisable(false, args); + break; + case "delete": + handleDelete(args); + break; + default: + help(); + break; + } + } + + /** + * EFFECTS: Go to main menu. + */ + @Override + public Screen exit() { + return Screen.MAIN; + } + + /** + * EFFECTS: Return "/templates/ %" + */ + @Override + public String getPS1() { + return "/templates/ %"; + } +} diff --git a/src/main/ui/tui/UIHandler.java b/src/main/ui/tui/UIHandler.java new file mode 100644 index 0000000..672fd50 --- /dev/null +++ b/src/main/ui/tui/UIHandler.java @@ -0,0 +1,45 @@ +package ui.tui; + +/** + * Represents a screen + */ +public interface UIHandler { + /** + * EFFECTS: Called when the screen is switched to. + */ + default void enter(Object... args) { + + } + + /** + * EFFECTS: Show objects. command() will not be called. + */ + void show(); + + /** + * EFFECTS: Commit changes and exit. command() will not be called. + */ + default void commit() { + } + + /** + * EFFECTS: Discard changes and exit. command() will not be called. Returns the next screen. + */ + Screen exit(); + + /** + * EFFECTS: Run help. command() will not be called. + */ + void help(); + + /** + * EFFECTS: Any commands rather than commit / exit / help. + * REQUIRES: args != null && args.length >= 1 + */ + void command(String... args); + + /** + * EFFECTS: Return the current PS1 prompt. + */ + String getPS1(); +} |