package mixconfig.panels.smartcard; import groovy.beans.Bindable; import java.beans.*; import mixconfig.tools.dataretention.smartcard.ISmartCard; import mixconfig.tools.dataretention.smartcard.ConnectionManager; import mixconfig.tools.dataretention.smartcard.commands.ErrorCodeException; // TODO: Rename to SmartCardSettingsModel /** * Abstract form model. Base for all models which need validation. * * The concrete class needs to have the @Bindable annotation and additionally needs a Map * "constraints" which can define validation closures for each model field. * * In case a property is a composite property (i.e. a list or a map), make sure to use * java.beans.ObservableList and the like. */ abstract class AbstractFormModel implements Cloneable { /** * Validate the current model and return validation errors. * The return format is as follows: * Field Name -> List of error message strings for this field name. * * @return Map A Map with validation errors for each field name where an error occured, or the empty map, if no errors occured. */ public Map> validate() { Map results = [:]; constraints.each { propertyName, constraint -> def result = constraint() if (result != null && result.size() > 0) { results[propertyName] = result } } return results } /** * Clones the form model, and returns a DEEP CLONE. * * Additionally, all ObservableLists are converted to normal Lists. * * @return def The cloned object. */ public def clone() { def clonedInstance = this.metaClass.properties .findAll(){ it.getSetter() != null } .inject(this.class.newInstance()){ clonedInstance, metaProperty -> if (metaProperty.name == 'metaClass') return clonedInstance; def singleProperty = metaProperty.getProperty(this); // In case it is a list, do a deep clone of every list element if (singleProperty instanceof List) { def newProperty = [] singleProperty.each { it -> newProperty << it.clone() } singleProperty = newProperty } metaProperty.setProperty(clonedInstance, singleProperty); return clonedInstance; } return clonedInstance } } /** * The main form model for the SmartCard installation wizard */ @Bindable class InstallationWizardFormModel extends AbstractFormModel { protected def constraints = [ neededNumberOfAdministrators: { if (this.neededNumberOfAdministrators > this.administrators.size()) { return ["Needed number of administrators must be smaller or equal than the total number of administrators!"] } return null }, neededNumberOfOperators: { if (this.neededNumberOfOperators > this.operators.size()) { return ["Needed number of operators must be smaller or equal than the total number of operators!"] } return null }, neededNumberOfTimeServers: { if (this.neededNumberOfTimeServers > this.timeServers.size()) { return ["Needed number of time servers must be smaller or equal than the total number of time servers!"] } }, GLOBAL: { def errors = [] def hasDuplicates = { list, identityProperty -> def duplicate = null; list.eachWithIndex{listElement, i -> if (i>0) { def listElementWithSameIdentity = list[0..i-1].find{secondListElement -> listElement[identityProperty] == secondListElement[identityProperty]}; if (listElementWithSameIdentity != null) { duplicate = listElement[identityProperty] } } } return duplicate; } def possibleDuplicateName = hasDuplicates(administrators, 'name') if (possibleDuplicateName) { errors << "Duplicate administrator with name ${possibleDuplicateName}." } } ] int neededNumberOfAdministrators = 1 boolean allowedToModifyAdministrators = true def administrators = new ObservableList([]) int neededNumberOfOperators = 1 boolean allowedToModifyOperators = false def operators = new ObservableList([]) int neededNumberOfTimeServers = 1 boolean allowedToModifyTimeServers = false def timeServers = new ObservableList([new TimeServer(), new TimeServer()]) public def getChangesFrom(InstallationWizardFormModel originalModel) { def changes = [] if (originalModel['neededNumberOfAdministrators'] != this['neededNumberOfAdministrators']) { changes << new Command( explanation: "Needed number of administrators was changed from ${originalModel.neededNumberOfAdministrators} to ${this.neededNumberOfAdministrators}", methodToExecute: {smartCard -> smartCard.setNeededNumberOfAdministrators(this['neededNumberOfAdministrators'])} ) } if (originalModel['neededNumberOfOperators'] != this['neededNumberOfOperators']) { changes << new Command( explanation: "Needed number of operators was changed from ${originalModel.neededNumberOfOperators} to ${this.neededNumberOfOperators}", methodToExecute: {smartCard -> smartCard.setNeededNumberOfOperators(this['neededNumberOfOperators'])} ) } // TODO: boolean properties etc // Simple properties /*['neededNumberOfAdministrators' , 'allowedToModifyAdministrators', 'neededNumberOfOperators', 'allowedToModifyOperators', 'neededNumberOfTimeServers', 'allowedToModifyTimeServers'].each { propertyName -> if (originalModel[propertyName] != this[propertyName]) { changes << changes[propertyName] = [old: originalModel[propertyName], 'new': this[propertyName]] } } */ // Complex list properties ['administrators', 'operators', 'timeServers'].each { propertyName -> } return changes } public void saveToCard() { ISmartCard smartCard = ConnectionManager.getInstance().connect(); smartCard.startTransaction() administrators.each {user -> smartCard.addAdministrator(user.name, user.password) } smartCard.setNeededNumberOfAdministrators(neededNumberOfAdministrators) operators.each {user -> smartCard.addOperator(user.name, user.password) } smartCard.setNeededNumberOfOperators(neededNumberOfOperators) smartCard.setPermissionChangeSettings(allowedToModifyAdministrators, allowedToModifyOperators, allowedToModifyTimeServers) smartCard.commitTransaction(null) } public void loadFromCard(ISmartCard smartCard) { smartCard.getAdministrators().each { name -> def administrator = new Administrator() administrator.name = name administrator.password = '000000' administrators << administrator } smartCard.getOperators().each { name -> def operator = new Operator() operator.name = name operator.password = '0000' operators << operator } // TODO: "key Numbers" allowedToModifyAdministrators = smartCard.isAllowedToModifyAdministrators() allowedToModifyOperators = smartCard.isAllowedToModifyOperators() allowedToModifyTimeServers = smartCard.isAllowedToModifyTimeServers() } } /** * The user model for the SmartCard installation wizard */ @Bindable abstract class User extends AbstractFormModel { protected def constraints = [ name: { def errors = [] if (password.length() != lengthOfPassword) { errors << "The password length of ${roleName} ${name} is not correct. Expected: ${lengthOfPassword}; Current length: ${password.length()}" } if (!password.isInteger()) { errors << "The password of ${roleName} ${name} has to consist only of digits." } return errors } ] // TODO: passwords need to be integer!! String name = "Hallo" String password = "" public boolean equals(User otherUser) { if (otherUser.name == name && otherUser.password == password) { return true; } return false; } } @Bindable class Administrator extends User { final String roleName = "administrator" final int lengthOfPassword = 6; } @Bindable class Operator extends User { final String roleName = "operator" final int lengthOfPassword = 4; } /** * The time server model for the SmartCard installation wizard */ @Bindable class TimeServer extends AbstractFormModel { protected def constraints = [] String uri = 'http://www.inf.tu-dresden.de' String certificate } /** * Command which needs to be performed after changes to the GUI */ class Command { String explanation Closure methodToExecute }