[Major] Rewrote privilege XML to use SAX for writing and parsing

This commit is contained in:
Robert von Burg 2023-09-19 09:49:35 +02:00
parent 96b24ca03d
commit bcba0264e4
Signed by: eitch
GPG Key ID: 75DB9C85C74331F7
21 changed files with 738 additions and 660 deletions

View File

@ -104,7 +104,7 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
this.persistenceHandler.replaceUser(user);
if (this.autoPersistOnUserChangesData)
this.persistenceHandler.persist();
persistModelAsync();
return user;

View File

@ -19,9 +19,9 @@ import li.strolch.privilege.base.*;
import li.strolch.privilege.model.*;
import li.strolch.privilege.model.internal.*;
import li.strolch.privilege.policy.PrivilegePolicy;
import li.strolch.privilege.xml.CertificateStubsDomWriter;
import li.strolch.privilege.xml.CertificateStubsSaxReader;
import li.strolch.privilege.xml.CertificateStubsSaxReader.CertificateStub;
import li.strolch.privilege.xml.CertificateStubsSaxWriter;
import li.strolch.utils.collections.Tuple;
import li.strolch.utils.concurrent.ElementLockingHandler;
import li.strolch.utils.dbc.DBC;
@ -31,7 +31,9 @@ import org.slf4j.LoggerFactory;
import org.xml.sax.SAXParseException;
import javax.crypto.SecretKey;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
@ -46,6 +48,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.text.MessageFormat.format;
import static java.util.Comparator.comparing;
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
import static li.strolch.utils.helper.StringHelper.*;
@ -336,6 +339,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
result.add(user.asUserRep());
}
result.sort(comparing(UserRep::getUsername));
return result;
}
@ -1571,7 +1575,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
user.getLocale(), userRoles, new HashMap<>(user.getProperties()));
}
private synchronized void persistModelAsync() {
protected synchronized void persistModelAsync() {
if (!this.autoPersistOnUserChangesData)
return;
@ -1605,7 +1609,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
try (OutputStream out = Files.newOutputStream(this.persistSessionsPath.toPath());
OutputStream outputStream = AesCryptoHelper.wrapEncrypt(this.secretKey, out)) {
CertificateStubsDomWriter writer = new CertificateStubsDomWriter(sessions, outputStream);
CertificateStubsSaxWriter writer = new CertificateStubsSaxWriter(sessions, outputStream);
writer.write();
outputStream.flush();
@ -1986,7 +1990,11 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_ACTION, PRIVILEGE_ACTION_PERSIST));
// persist non async
return this.persistenceHandler.persist();
try {
return this.persistenceHandler.persist();
} catch (XMLStreamException | IOException e) {
throw new IllegalStateException("Failed to persist model", e);
}
}
@Override

View File

@ -15,6 +15,7 @@
*/
package li.strolch.privilege.handler;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@ -24,6 +25,8 @@ import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.policy.PrivilegePolicy;
import javax.xml.stream.XMLStreamException;
/**
* <p>
* The {@link PersistenceHandler} takes care of retrieving and persisting model objects to the underlying database. This
@ -131,7 +134,7 @@ public interface PersistenceHandler {
*
* @return true if changes were persisted successfully, false if nothing needed to be persisted
*/
boolean persist();
boolean persist() throws XMLStreamException, IOException;
/**
* Informs this {@link PersistenceHandler} to reload the data from the backend

View File

@ -19,15 +19,17 @@ import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.xml.PrivilegeRolesDomWriter;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader;
import li.strolch.privilege.xml.PrivilegeUsersDomWriter;
import li.strolch.privilege.xml.PrivilegeRolesSaxWriter;
import li.strolch.privilege.xml.PrivilegeUsersSaxReader;
import li.strolch.privilege.xml.PrivilegeUsersSaxWriter;
import li.strolch.utils.helper.XmlHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -259,14 +261,14 @@ public class XmlPersistenceHandler implements PersistenceHandler {
* Writes the model to the XML files. Where the files are written to was defined in the {@link #initialize(Map)}
*/
@Override
public boolean persist() {
public boolean persist() throws XMLStreamException, IOException {
long start = System.nanoTime();
boolean saved = false;
// write users file
if (this.userMapDirty) {
// delegate writing
PrivilegeUsersDomWriter modelWriter = new PrivilegeUsersDomWriter(getAllUsers(), this.usersPath);
PrivilegeUsersSaxWriter modelWriter = new PrivilegeUsersSaxWriter(getAllUsers(), this.usersPath);
modelWriter.write();
this.userMapDirty = false;
@ -276,7 +278,7 @@ public class XmlPersistenceHandler implements PersistenceHandler {
// write roles file
if (this.roleMapDirty) {
// delegate writing
PrivilegeRolesDomWriter modelWriter = new PrivilegeRolesDomWriter(getAllRoles(), this.rolesPath);
PrivilegeRolesSaxWriter modelWriter = new PrivilegeRolesSaxWriter(getAllRoles(), this.rolesPath);
modelWriter.write();
this.roleMapDirty = false;

View File

@ -1,18 +1,20 @@
package li.strolch.privilege.helper;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader;
import li.strolch.privilege.xml.PrivilegeRolesSaxWriter;
import li.strolch.utils.helper.XmlHelper;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import li.strolch.privilege.model.internal.Role;
import li.strolch.privilege.xml.PrivilegeRolesDomWriter;
import li.strolch.privilege.xml.PrivilegeRolesSaxReader;
import li.strolch.utils.helper.XmlHelper;
public class WriteRolesFileHelper {
public static void main(String[] args) {
public static void main(String[] args) throws XMLStreamException, IOException {
if (args.length != 2)
throw new IllegalStateException("Usage: <src> <dst>");
@ -31,7 +33,7 @@ public class WriteRolesFileHelper {
Map<String, Role> rolesMap = xmlHandler.getRoles();
List<Role> roles = new ArrayList<>(rolesMap.values());
PrivilegeRolesDomWriter configSaxWriter = new PrivilegeRolesDomWriter(roles, dst);
PrivilegeRolesSaxWriter configSaxWriter = new PrivilegeRolesSaxWriter(roles, dst);
configSaxWriter.write();
}
}

View File

@ -0,0 +1,67 @@
package li.strolch.privilege.helper;
import javanet.staxutils.IndentingXMLStreamWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static li.strolch.privilege.helper.XmlConstants.*;
public class XmlHelper {
public static void writeStringMapElement(XMLStreamWriter xmlWriter, Map<String, String> parameterMap,
String elementName, String valueElementName) throws XMLStreamException {
writeStringMapElement(xmlWriter, parameterMap, elementName, valueElementName, XML_ATTR_VALUE);
}
public static void writeStringMapElement(XMLStreamWriter xmlWriter, Map<String, String> parameterMap,
String elementName, String valueElementName, String valueAttrName) throws XMLStreamException {
if (parameterMap == null || parameterMap.isEmpty())
return;
xmlWriter.writeStartElement(elementName);
List<String> propertyKeys = new ArrayList<>(parameterMap.keySet());
propertyKeys.sort(null);
for (String propertyKey : propertyKeys) {
xmlWriter.writeEmptyElement(valueElementName);
xmlWriter.writeAttribute(XML_ATTR_NAME, propertyKey);
xmlWriter.writeAttribute(valueAttrName, parameterMap.get(propertyKey));
}
xmlWriter.writeEndElement();
}
public static void writeStringList(IndentingXMLStreamWriter xmlWriter, String elementName, Set<String> values)
throws XMLStreamException {
List<String> denyList = new ArrayList<>(values);
denyList.sort(null);
for (String value : denyList) {
writeStringElement(xmlWriter, elementName, value);
}
}
public static void writeStringElement(IndentingXMLStreamWriter xmlWriter, String elementName, String value)
throws XMLStreamException {
xmlWriter.writeStartElement(elementName);
xmlWriter.writeCharacters(value);
xmlWriter.writeEndElement();
}
public static IndentingXMLStreamWriter openXmlStreamWriterDocument(Writer ioWriter) throws XMLStreamException {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
IndentingXMLStreamWriter xmlWriter = new IndentingXMLStreamWriter(factory.createXMLStreamWriter(ioWriter));
xmlWriter.setIndent(" ");
// create document root
xmlWriter.writeStartDocument(StandardCharsets.UTF_8.name(), "1.0");
return xmlWriter;
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.io.OutputStream;
import java.util.List;
import li.strolch.privilege.model.Certificate;
import li.strolch.utils.helper.XmlHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class CertificateStubsDomWriter {
private final List<Certificate> certificates;
private final OutputStream outputStream;
public CertificateStubsDomWriter(List<Certificate> certificates, OutputStream outputStream) {
this.certificates = certificates;
this.outputStream = outputStream;
}
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XML_ROOT_CERTIFICATES);
doc.appendChild(rootElement);
this.certificates.stream().sorted(comparing(Certificate::getSessionId)).forEach(cert -> {
// create the certificate element
Element certElement = doc.createElement(XML_CERTIFICATE);
rootElement.appendChild(certElement);
// sessionId;
certElement.setAttribute(XML_ATTR_SESSION_ID, cert.getSessionId());
// usage;
certElement.setAttribute(XML_ATTR_USAGE, cert.getUsage().name());
// username;
certElement.setAttribute(XML_ATTR_USERNAME, cert.getUsername());
// authToken;
certElement.setAttribute(XML_ATTR_AUTH_TOKEN, cert.getAuthToken());
// source;
certElement.setAttribute(XML_ATTR_SOURCE, cert.getSource());
// locale;
certElement.setAttribute(XML_ATTR_LOCALE, cert.getLocale().toLanguageTag());
// loginTime;
certElement.setAttribute(XML_ATTR_LOGIN_TIME, ISO8601.toString(cert.getLoginTime()));
// lastAccess;
certElement.setAttribute(XML_ATTR_LAST_ACCESS, ISO8601.toString(cert.getLastAccess()));
// keepAlive;
certElement.setAttribute(XML_ATTR_KEEP_ALIVE, String.valueOf(cert.isKeepAlive()));
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.outputStream);
}
}

View File

@ -15,16 +15,6 @@
*/
package li.strolch.privilege.xml;
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.SOURCE_UNKNOWN;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.isEmpty;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import li.strolch.privilege.base.PrivilegeException;
import li.strolch.privilege.model.Usage;
import li.strolch.utils.dbc.DBC;
@ -33,6 +23,16 @@ import li.strolch.utils.iso8601.ISO8601;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.SOURCE_UNKNOWN;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.utils.helper.StringHelper.isEmpty;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -55,33 +55,27 @@ public class CertificateStubsSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) {
switch (qName) {
case XML_ROOT_CERTIFICATES:
break;
case XML_CERTIFICATE:
CertificateStub stub = new CertificateStub();
stub.usage = Usage.valueOf(attributes.getValue(XML_ATTR_USAGE).trim());
stub.sessionId = attributes.getValue(XML_ATTR_SESSION_ID).trim();
stub.username = attributes.getValue(XML_ATTR_USERNAME).trim();
stub.authToken = attributes.getValue(XML_ATTR_AUTH_TOKEN).trim();
stub.source = attributes.getValue(XML_ATTR_SOURCE).trim();
stub.locale = Locale.forLanguageTag(attributes.getValue(XML_ATTR_LOCALE).trim());
stub.loginTime = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LOGIN_TIME).trim());
stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LAST_ACCESS).trim());
stub.keepAlive = Boolean.parseBoolean(attributes.getValue(XML_ATTR_KEEP_ALIVE).trim());
DBC.INTERIM.assertNotEmpty("sessionId missing on sessions data!", stub.sessionId);
DBC.INTERIM.assertNotEmpty("username missing on sessions data!", stub.username);
DBC.INTERIM.assertNotEmpty("authToken missing on sessions data!", stub.authToken);
if (isEmpty(stub.source))
stub.source = SOURCE_UNKNOWN;
this.stubs.add(stub);
break;
default:
throw new PrivilegeException("Unhandled tag " + qName);
case XML_ROOT_CERTIFICATES -> {
}
case XML_CERTIFICATE -> {
CertificateStub stub = new CertificateStub();
stub.usage = Usage.valueOf(attributes.getValue(XML_ATTR_USAGE).trim());
stub.sessionId = attributes.getValue(XML_ATTR_SESSION_ID).trim();
stub.username = attributes.getValue(XML_ATTR_USERNAME).trim();
stub.authToken = attributes.getValue(XML_ATTR_AUTH_TOKEN).trim();
stub.source = attributes.getValue(XML_ATTR_SOURCE).trim();
stub.locale = Locale.forLanguageTag(attributes.getValue(XML_ATTR_LOCALE).trim());
stub.loginTime = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LOGIN_TIME).trim());
stub.lastAccess = ISO8601.parseToZdt(attributes.getValue(XML_ATTR_LAST_ACCESS).trim());
stub.keepAlive = Boolean.parseBoolean(attributes.getValue(XML_ATTR_KEEP_ALIVE).trim());
DBC.INTERIM.assertNotEmpty("sessionId missing on sessions data!", stub.sessionId);
DBC.INTERIM.assertNotEmpty("username missing on sessions data!", stub.username);
DBC.INTERIM.assertNotEmpty("authToken missing on sessions data!", stub.authToken);
if (isEmpty(stub.source))
stub.source = SOURCE_UNKNOWN;
this.stubs.add(stub);
}
default -> throw new PrivilegeException("Unhandled tag " + qName);
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.Certificate;
import li.strolch.utils.iso8601.ISO8601;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.openXmlStreamWriterDocument;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class CertificateStubsSaxWriter {
private final List<Certificate> certificates;
private final OutputStream outputStream;
public CertificateStubsSaxWriter(List<Certificate> certificates, OutputStream outputStream) {
this.certificates = certificates;
this.outputStream = outputStream;
}
public void write() throws IOException, XMLStreamException {
Writer ioWriter = new OutputStreamWriter(this.outputStream, StandardCharsets.UTF_8);
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(XML_ROOT_CERTIFICATES);
List<Certificate> certificates = new ArrayList<>(this.certificates);
certificates.sort(comparing(Certificate::getSessionId));
for (Certificate cert : certificates) {
// create the certificate element
xmlWriter.writeStartElement(XML_CERTIFICATE);
// sessionId;
xmlWriter.writeAttribute(XML_ATTR_SESSION_ID, cert.getSessionId());
// usage;
xmlWriter.writeAttribute(XML_ATTR_USAGE, cert.getUsage().name());
// username;
xmlWriter.writeAttribute(XML_ATTR_USERNAME, cert.getUsername());
// authToken;
xmlWriter.writeAttribute(XML_ATTR_AUTH_TOKEN, cert.getAuthToken());
// source;
xmlWriter.writeAttribute(XML_ATTR_SOURCE, cert.getSource());
// locale;
xmlWriter.writeAttribute(XML_ATTR_LOCALE, cert.getLocale().toLanguageTag());
// loginTime;
xmlWriter.writeAttribute(XML_ATTR_LOGIN_TIME, ISO8601.toString(cert.getLoginTime()));
// lastAccess;
xmlWriter.writeAttribute(XML_ATTR_LAST_ACCESS, ISO8601.toString(cert.getLastAccess()));
// keepAlive;
xmlWriter.writeAttribute(XML_ATTR_KEEP_ALIVE, String.valueOf(cert.isKeepAlive()));
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}

View File

@ -17,7 +17,6 @@ package li.strolch.privilege.xml;
import org.xml.sax.Attributes;
// TODO write JavaDoc...
public interface ElementParser {
void startElement(String uri, String localName, String qName, Attributes attributes);

View File

@ -1,129 +0,0 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.io.File;
import java.util.Map;
import java.util.Map.Entry;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import li.strolch.utils.helper.XmlHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeConfigDomWriter {
private final PrivilegeContainerModel containerModel;
private final File configFile;
/**
*
*/
public PrivilegeConfigDomWriter(PrivilegeContainerModel containerModel, File configFile) {
this.containerModel = containerModel;
this.configFile = configFile;
}
/**
*
*/
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XML_ROOT_PRIVILEGE);
doc.appendChild(rootElement);
Element containerElement = doc.createElement(XML_CONTAINER);
rootElement.appendChild(containerElement);
// create EncryptionHandler
Element encryptionHandlerElem = doc.createElement(XML_HANDLER_ENCRYPTION);
containerElement.appendChild(encryptionHandlerElem);
encryptionHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getEncryptionHandlerClassName());
// Parameters
fillParameterMap(doc, encryptionHandlerElem, this.containerModel.getEncryptionHandlerParameterMap());
// create PersistenceHandler
Element persistenceHandlerElem = doc.createElement(XML_HANDLER_PERSISTENCE);
containerElement.appendChild(persistenceHandlerElem);
persistenceHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getPersistenceHandlerClassName());
// Parameters
fillParameterMap(doc, persistenceHandlerElem, this.containerModel.getPersistenceHandlerParameterMap());
// Parameters
fillParameterMap(doc, containerElement, this.containerModel.getParameterMap());
// create UserChallengeHandler
Element userChallengeHandlerElem = doc.createElement(XML_HANDLER_USER_CHALLENGE);
containerElement.appendChild(userChallengeHandlerElem);
userChallengeHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getUserChallengeHandlerClassName());
// Parameters
fillParameterMap(doc, userChallengeHandlerElem, this.containerModel.getUserChallengeHandlerParameterMap());
// create SingleSignOnHandler
if (this.containerModel.getSsoHandlerClassName() != null) {
Element ssoHandlerElem = doc.createElement(XML_HANDLER_SSO);
containerElement.appendChild(ssoHandlerElem);
ssoHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getSsoHandlerClassName());
// Parameters
fillParameterMap(doc, ssoHandlerElem, this.containerModel.getSsoHandlerParameterMap());
}
// create PrivilegeHandler
if (this.containerModel.getSsoHandlerClassName() != null) {
Element privilegeHandlerElem = doc.createElement(XML_HANDLER_PRIVILEGE);
containerElement.appendChild(privilegeHandlerElem);
privilegeHandlerElem.setAttribute(XML_ATTR_CLASS, this.containerModel.getPrivilegeHandlerClassName());
// Parameters
fillParameterMap(doc, privilegeHandlerElem, this.containerModel.getPrivilegeHandlerParameterMap());
}
// Policies
Element policiesElem = doc.createElement(XML_POLICIES);
rootElement.appendChild(policiesElem);
this.containerModel.getPolicies().entrySet().stream().sorted(Entry.comparingByKey())
.forEach(entry -> {
Element policyElem = doc.createElement(XML_POLICY);
policyElem.setAttribute(XML_ATTR_NAME, entry.getKey());
policyElem.setAttribute(XML_ATTR_CLASS, entry.getValue().getName());
policiesElem.appendChild(policyElem);
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.configFile);
}
private void fillParameterMap(Document doc, Element parent, Map<String, String> parameterMap) {
if (parameterMap != null && !parameterMap.isEmpty()) {
Element parametersElement = doc.createElement(XML_PARAMETERS);
for (Entry<String, String> entry : parameterMap.entrySet()) {
Element parameterElement = doc.createElement(XML_PARAMETER);
parameterElement.setAttribute(XML_ATTR_NAME, entry.getKey());
parameterElement.setAttribute(XML_ATTR_VALUE, entry.getValue());
parametersElement.appendChild(parameterElement);
}
parent.appendChild(parametersElement);
}
}
}

View File

@ -15,18 +15,17 @@
*/
package li.strolch.privilege.xml;
import static li.strolch.privilege.helper.XmlConstants.*;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import static li.strolch.privilege.helper.XmlConstants.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
@ -48,12 +47,12 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
switch (qName) {
case XML_CONTAINER -> this.buildersStack.push(new ContainerParser());
case XML_PARAMETERS -> this.buildersStack.push(new ParametersParser());
case XML_POLICIES -> this.buildersStack.push(new PoliciesParser());
default -> {
// nothing to do, probably handle on stack
}
case XML_CONTAINER -> this.buildersStack.push(new ContainerParser());
case XML_PARAMETERS -> this.buildersStack.push(new ParametersParser());
case XML_POLICIES -> this.buildersStack.push(new PoliciesParser());
default -> {
// nothing to do, probably handle on stack
}
}
if (!this.buildersStack.isEmpty())
@ -89,38 +88,38 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
public void startElement(String uri, String localName, String qName, Attributes attributes) {
switch (qName) {
case XML_CONTAINER -> this.currentElement = qName;
case XML_HANDLER_ENCRYPTION -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setEncryptionHandlerClassName(className);
}
case XML_HANDLER_PASSWORD_STRENGTH -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPasswordStrengthHandlerClassName(className);
}
case XML_HANDLER_PERSISTENCE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPersistenceHandlerClassName(className);
}
case XML_HANDLER_USER_CHALLENGE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setUserChallengeHandlerClassName(className);
}
case XML_HANDLER_SSO -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setSsoHandlerClassName(className);
}
case XML_HANDLER_PRIVILEGE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPrivilegeHandlerClassName(className);
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
case XML_CONTAINER -> this.currentElement = qName;
case XML_HANDLER_PRIVILEGE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPrivilegeHandlerClassName(className);
}
case XML_HANDLER_ENCRYPTION -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setEncryptionHandlerClassName(className);
}
case XML_HANDLER_PASSWORD_STRENGTH -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPasswordStrengthHandlerClassName(className);
}
case XML_HANDLER_PERSISTENCE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setPersistenceHandlerClassName(className);
}
case XML_HANDLER_USER_CHALLENGE -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setUserChallengeHandlerClassName(className);
}
case XML_HANDLER_SSO -> {
this.currentElement = qName;
String className = attributes.getValue(XML_ATTR_CLASS).trim();
getContainerModel().setSsoHandlerClassName(className);
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
}
}
@ -129,20 +128,17 @@ public class PrivilegeConfigSaxReader extends DefaultHandler {
if (!(child instanceof ParametersParser parametersChild))
return;
final Map<String, String> params = parametersChild.getParameterMap();
switch (this.currentElement) {
case XML_CONTAINER -> getContainerModel().setParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_ENCRYPTION ->
getContainerModel().setEncryptionHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PASSWORD_STRENGTH ->
getContainerModel().setPasswordStrengthHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PERSISTENCE ->
getContainerModel().setPersistenceHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_USER_CHALLENGE ->
getContainerModel().setUserChallengeHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_SSO -> getContainerModel().setSsoHandlerParameterMap(parametersChild.getParameterMap());
case XML_HANDLER_PRIVILEGE ->
getContainerModel().setPrivilegeHandlerParameterMap(parametersChild.getParameterMap());
default -> throw new IllegalStateException("Unexpected value: " + this.currentElement);
case XML_CONTAINER -> getContainerModel().setParameterMap(params);
case XML_HANDLER_PRIVILEGE -> getContainerModel().setPrivilegeHandlerParameterMap(params);
case XML_HANDLER_ENCRYPTION -> getContainerModel().setEncryptionHandlerParameterMap(params);
case XML_HANDLER_PASSWORD_STRENGTH ->
getContainerModel().setPasswordStrengthHandlerParameterMap(params);
case XML_HANDLER_PERSISTENCE -> getContainerModel().setPersistenceHandlerParameterMap(params);
case XML_HANDLER_USER_CHALLENGE -> getContainerModel().setUserChallengeHandlerParameterMap(params);
case XML_HANDLER_SSO -> getContainerModel().setSsoHandlerParameterMap(params);
default -> throw new IllegalStateException("Unexpected value: " + this.currentElement);
}
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.internal.PrivilegeContainerModel;
import javax.xml.stream.XMLStreamException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.openXmlStreamWriterDocument;
import static li.strolch.privilege.helper.XmlHelper.writeStringMapElement;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeConfigSaxWriter {
private final PrivilegeContainerModel containerModel;
private final File configFile;
public PrivilegeConfigSaxWriter(PrivilegeContainerModel containerModel, File configFile) {
this.containerModel = containerModel;
this.configFile = configFile;
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.configFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(XML_ROOT_PRIVILEGE);
// write container element
xmlWriter.writeStartElement(XML_CONTAINER);
writeStringMapElement(xmlWriter, this.containerModel.getParameterMap(), XML_PARAMETERS, XML_PARAMETER);
{
// write PrivilegeHandler
if (this.containerModel.getPrivilegeHandlerClassName() != null)
writeHandler(xmlWriter, XML_HANDLER_PRIVILEGE, this.containerModel.getPrivilegeHandlerClassName(),
this.containerModel.getPrivilegeHandlerParameterMap());
// write EncryptionHandler
writeHandler(xmlWriter, XML_HANDLER_ENCRYPTION, this.containerModel.getEncryptionHandlerClassName(),
this.containerModel.getEncryptionHandlerParameterMap());
// write PersistenceHandler
writeHandler(xmlWriter, XML_HANDLER_PERSISTENCE, this.containerModel.getPersistenceHandlerClassName(),
this.containerModel.getPersistenceHandlerParameterMap());
// write PasswordStrengthHandler
if (this.containerModel.getPasswordStrengthHandlerClassName() != null)
writeHandler(xmlWriter, XML_HANDLER_PASSWORD_STRENGTH,
this.containerModel.getPasswordStrengthHandlerClassName(),
this.containerModel.getPasswordStrengthHandlerParameterMap());
// write UserChallengeHandler
if (this.containerModel.getUserChallengeHandlerClassName() != null)
writeHandler(xmlWriter, XML_HANDLER_USER_CHALLENGE,
this.containerModel.getUserChallengeHandlerClassName(),
this.containerModel.getUserChallengeHandlerParameterMap());
// write SingleSignOnHandler
if (this.containerModel.getSsoHandlerClassName() != null)
writeHandler(xmlWriter, XML_HANDLER_SSO, this.containerModel.getSsoHandlerClassName(),
this.containerModel.getSsoHandlerParameterMap());
}
xmlWriter.writeEndElement();
// Policies
Map<String, String> policies = new HashMap<>();
this.containerModel.getPolicies().forEach((key, value) -> policies.put(key, value.getName()));
writeStringMapElement(xmlWriter, policies, XML_POLICIES, XML_POLICY, XML_ATTR_CLASS);
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
private void writeHandler(IndentingXMLStreamWriter xmlWriter, String handleName, String className,
Map<String, String> parameters) throws XMLStreamException {
if (parameters.isEmpty())
xmlWriter.writeEmptyElement(handleName);
else
xmlWriter.writeStartElement(handleName);
xmlWriter.writeAttribute(XML_ATTR_CLASS, className);
writeStringMapElement(xmlWriter, parameters, XML_PARAMETERS, XML_PARAMETER);
if (!parameters.isEmpty())
xmlWriter.writeEndElement();
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import static java.util.Comparator.comparing;
import java.io.File;
import java.util.List;
import java.util.Locale;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.Role;
import li.strolch.utils.helper.XmlHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeRolesDomWriter {
private final List<Role> roles;
private final File modelFile;
public PrivilegeRolesDomWriter(List<Role> roles, File modelFile) {
this.roles = roles;
this.modelFile = modelFile;
}
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XmlConstants.XML_ROLES);
doc.appendChild(rootElement);
this.roles.stream().sorted(comparing(role1 -> role1.getName().toLowerCase(Locale.ROOT))).forEach(role -> {
// create the role element
Element roleElement = doc.createElement(XmlConstants.XML_ROLE);
rootElement.appendChild(roleElement);
roleElement.setAttribute(XmlConstants.XML_ATTR_NAME, role.getName());
role.getPrivilegeNames().stream().sorted().map(role::getPrivilege).forEach(privilege -> {
Element privilegeElement = doc.createElement(XmlConstants.XML_PRIVILEGE);
roleElement.appendChild(privilegeElement);
privilegeElement.setAttribute(XmlConstants.XML_ATTR_NAME, privilege.getName());
privilegeElement.setAttribute(XmlConstants.XML_ATTR_POLICY, privilege.getPolicy());
if (privilege.isAllAllowed()) {
Element allAllowedElement = doc.createElement(XmlConstants.XML_ALL_ALLOWED);
allAllowedElement.setTextContent(Boolean.toString(privilege.isAllAllowed()));
privilegeElement.appendChild(allAllowedElement);
}
privilege.getDenyList().stream().sorted().forEach(denyValue -> {
Element denyValueElement = doc.createElement(XmlConstants.XML_DENY);
denyValueElement.setTextContent(denyValue);
privilegeElement.appendChild(denyValueElement);
});
privilege.getAllowList().stream().sorted().forEach(allowValue -> {
Element allowValueElement = doc.createElement(XmlConstants.XML_ALLOW);
allowValueElement.setTextContent(allowValue);
privilegeElement.appendChild(allowValueElement);
});
});
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.modelFile);
}
}

View File

@ -15,10 +15,6 @@
*/
package li.strolch.privilege.xml;
import java.text.MessageFormat;
import java.util.*;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.internal.PrivilegeImpl;
import li.strolch.privilege.model.internal.Role;
@ -29,6 +25,11 @@ import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.text.MessageFormat;
import java.util.*;
import static li.strolch.privilege.helper.XmlConstants.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -51,9 +52,13 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals(XmlConstants.XML_ROLE)) {
if (qName.equals(XML_ROLE)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(RoleParser.class)))
throw new IllegalArgumentException("Previous Role not closed!");
this.buildersStack.push(new RoleParser());
} else if (qName.equals(XmlConstants.XML_PROPERTIES)) {
} else if (qName.equals(XML_PROPERTIES)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(PropertyParser.class)))
throw new IllegalArgumentException("Previous Properties not closed!");
this.buildersStack.push(new PropertyParser());
}
@ -74,9 +79,9 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.buildersStack.peek().endElement(uri, localName, qName);
ElementParser elementParser = null;
if (qName.equals(XmlConstants.XML_ROLE)) {
if (qName.equals(XML_ROLE)) {
elementParser = this.buildersStack.pop();
} else if (qName.equals(XmlConstants.XML_PROPERTIES)) {
} else if (qName.equals(XML_PROPERTIES)) {
elementParser = this.buildersStack.pop();
}
@ -134,20 +139,15 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
this.text = new StringBuilder();
switch (qName) {
case XmlConstants.XML_ROLE:
this.roleName = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
break;
case XmlConstants.XML_PRIVILEGE:
this.privilegeName = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
this.privilegePolicy = attributes.getValue(XmlConstants.XML_ATTR_POLICY).trim();
break;
case XmlConstants.XML_ALLOW:
case XmlConstants.XML_DENY:
case XmlConstants.XML_ALL_ALLOWED:
case XML_ROLE -> this.roleName = attributes.getValue(XML_ATTR_NAME).trim();
case XML_PRIVILEGE -> {
this.privilegeName = attributes.getValue(XML_ATTR_NAME).trim();
this.privilegePolicy = attributes.getValue(XML_ATTR_POLICY).trim();
}
case XML_ALLOW, XML_DENY, XML_ALL_ALLOWED -> {
}
// no-op
break;
default:
throw new IllegalArgumentException("Unhandled tag " + qName);
default -> throw new IllegalArgumentException("Unhandled tag " + qName);
}
}
@ -159,31 +159,33 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case XmlConstants.XML_ALL_ALLOWED ->
this.allAllowed = StringHelper.parseBoolean(this.text.toString().trim());
case XmlConstants.XML_ALLOW -> this.allowList.add(this.text.toString().trim());
case XmlConstants.XML_DENY -> this.denyList.add(this.text.toString().trim());
case XmlConstants.XML_PRIVILEGE -> {
IPrivilege privilege = new PrivilegeImpl(this.privilegeName, this.privilegePolicy, this.allAllowed,
this.denyList, this.allowList);
this.privileges.put(this.privilegeName, privilege);
this.privilegeName = null;
this.privilegePolicy = null;
this.allAllowed = false;
this.denyList = new HashSet<>();
this.allowList = new HashSet<>();
}
case XmlConstants.XML_ROLE -> {
Role role = new Role(this.roleName, this.privileges);
roles.put(role.getName(), role);
logger.info(MessageFormat.format("New Role: {0}", role));
init();
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
case XML_ALL_ALLOWED -> this.allAllowed = StringHelper.parseBoolean(getText());
case XML_ALLOW -> this.allowList.add(getText());
case XML_DENY -> this.denyList.add(getText());
case XML_PRIVILEGE -> {
IPrivilege privilege = new PrivilegeImpl(this.privilegeName, this.privilegePolicy, this.allAllowed,
this.denyList, this.allowList);
this.privileges.put(this.privilegeName, privilege);
this.privilegeName = null;
this.privilegePolicy = null;
this.allAllowed = false;
this.denyList = new HashSet<>();
this.allowList = new HashSet<>();
}
case XML_ROLE -> {
Role role = new Role(this.roleName, this.privileges);
roles.put(role.getName(), role);
logger.info(MessageFormat.format("New Role: {0}", role));
init();
}
default -> throw new IllegalStateException("Unexpected value: " + qName);
}
}
private String getText() {
return this.text.toString().trim();
}
}
static class PropertyParser extends ElementParserAdapter {
@ -194,11 +196,11 @@ public class PrivilegeRolesSaxReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (qName.equals(XmlConstants.XML_PROPERTY)) {
String key = attributes.getValue(XmlConstants.XML_ATTR_NAME).trim();
String value = attributes.getValue(XmlConstants.XML_ATTR_VALUE).trim();
if (qName.equals(XML_PROPERTY)) {
String key = attributes.getValue(XML_ATTR_NAME).trim();
String value = attributes.getValue(XML_ATTR_VALUE).trim();
this.parameterMap.put(key, value);
} else if (!qName.equals(XmlConstants.XML_PROPERTIES)) {
} else if (!qName.equals(XML_PROPERTIES)) {
throw new IllegalArgumentException("Unhandled tag " + qName);
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.internal.Role;
import javax.xml.stream.XMLStreamException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeRolesSaxWriter {
private final List<Role> roles;
private final File modelFile;
public PrivilegeRolesSaxWriter(List<Role> roles, File modelFile) {
this.roles = roles;
this.modelFile = modelFile;
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.modelFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(XML_ROLES);
List<Role> roles = new ArrayList<>(this.roles);
roles.sort(comparing(role1 -> role1.getName().toLowerCase(Locale.ROOT)));
for (Role role : roles) {
// start the role element
xmlWriter.writeStartElement(XML_ROLE);
xmlWriter.writeAttribute(XML_ATTR_NAME, role.getName());
List<String> privilegeNames = new ArrayList<>(role.getPrivilegeNames());
privilegeNames.sort(null);
for (String privilegeName : privilegeNames) {
IPrivilege privilege = role.getPrivilege(privilegeName);
xmlWriter.writeStartElement(XML_PRIVILEGE);
xmlWriter.writeAttribute(XML_ATTR_NAME, privilege.getName());
xmlWriter.writeAttribute(XML_ATTR_POLICY, privilege.getPolicy());
if (privilege.isAllAllowed())
writeStringElement(xmlWriter, XML_ALL_ALLOWED, "true");
writeStringList(xmlWriter, XML_DENY, privilege.getDenyList());
writeStringList(xmlWriter, XML_ALLOW, privilege.getAllowList());
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
}

View File

@ -1,165 +0,0 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import li.strolch.privilege.helper.XmlConstants;
import li.strolch.privilege.model.internal.PasswordCrypt;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.helper.StringHelper;
import li.strolch.utils.helper.XmlHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.io.File;
import java.util.List;
import java.util.Map;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.model.internal.PasswordCrypt.buildPasswordString;
import static li.strolch.utils.helper.StringHelper.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeUsersDomWriter {
private final List<User> users;
private final File modelFile;
public PrivilegeUsersDomWriter(List<User> users, File modelFile) {
this.users = users;
this.modelFile = modelFile;
this.users.sort(comparing(User::getUsername));
}
public void write() {
// create document root
Document doc = XmlHelper.createDocument();
Element rootElement = doc.createElement(XML_USERS);
doc.appendChild(rootElement);
this.users.forEach(user -> {
// create the user element
Element userElement = doc.createElement(XML_USER);
rootElement.appendChild(userElement);
userElement.setAttribute(XML_ATTR_USER_ID, user.getUserId());
userElement.setAttribute(XML_ATTR_USERNAME, user.getUsername());
writePassword(user, userElement);
// add first name element
if (isNotEmpty(user.getFirstname())) {
Element firstnameElement = doc.createElement(XML_FIRSTNAME);
firstnameElement.setTextContent(user.getFirstname());
userElement.appendChild(firstnameElement);
}
// add last name element
if (isNotEmpty(user.getLastname())) {
Element lastnameElement = doc.createElement(XML_LASTNAME);
lastnameElement.setTextContent(user.getLastname());
userElement.appendChild(lastnameElement);
}
// add state element
Element stateElement = doc.createElement(XML_STATE);
stateElement.setTextContent(user.getUserState().toString());
userElement.appendChild(stateElement);
// add locale element
Element localeElement = doc.createElement(XML_LOCALE);
localeElement.setTextContent(user.getLocale().toLanguageTag());
userElement.appendChild(localeElement);
// add password change requested element
if (user.isPasswordChangeRequested()) {
Element passwordChangeRequestedElement = doc.createElement(XML_PASSWORD_CHANGE_REQUESTED);
passwordChangeRequestedElement.setTextContent(Boolean.toString(true));
userElement.appendChild(passwordChangeRequestedElement);
}
// add all the role elements
Element rolesElement = doc.createElement(XML_ROLES);
userElement.appendChild(rolesElement);
user.getRoles().stream().sorted().forEach(roleName -> {
Element roleElement = doc.createElement(XML_ROLE);
roleElement.setTextContent(roleName);
rolesElement.appendChild(roleElement);
});
// add the parameters
if (!user.getProperties().isEmpty()) {
Element parametersElement = doc.createElement(XML_PROPERTIES);
userElement.appendChild(parametersElement);
user.getProperties().entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
Element paramElement = doc.createElement(XML_PROPERTY);
paramElement.setAttribute(XML_ATTR_NAME, entry.getKey());
paramElement.setAttribute(XML_ATTR_VALUE, entry.getValue());
parametersElement.appendChild(paramElement);
});
}
if (!user.isHistoryEmpty()) {
UserHistory history = user.getHistory();
Element historyElement = doc.createElement(XML_HISTORY);
userElement.appendChild(historyElement);
if (!history.isFirstLoginEmpty()) {
Element element = doc.createElement(XML_FIRST_LOGIN);
element.setTextContent(ISO8601.toString(history.getFirstLogin()));
historyElement.appendChild(element);
}
if (!history.isLastLoginEmpty()) {
Element element = doc.createElement(XML_LAST_LOGIN);
element.setTextContent(ISO8601.toString(history.getLastLogin()));
historyElement.appendChild(element);
}
if (!history.isLastPasswordChangeEmpty()) {
Element element = doc.createElement(XML_LAST_PASSWORD_CHANGE);
element.setTextContent(ISO8601.toString(history.getLastPasswordChange()));
historyElement.appendChild(element);
}
}
});
// write the container file to disk
XmlHelper.writeDocument(doc, this.modelFile);
}
private void writePassword(User user, Element userElement) {
PasswordCrypt passwordCrypt = user.getPasswordCrypt();
if (passwordCrypt == null)
return;
String passwordString = passwordCrypt.buildPasswordString();
if (passwordString != null) {
userElement.setAttribute(XML_ATTR_PASSWORD, passwordString);
} else {
if (passwordCrypt.getPassword() != null)
userElement.setAttribute(XML_ATTR_PASSWORD, toHexString(passwordCrypt.getPassword()));
if (passwordCrypt.getSalt() != null)
userElement.setAttribute(XML_ATTR_SALT, toHexString(passwordCrypt.getSalt()));
}
}
}

View File

@ -15,16 +15,10 @@
*/
package li.strolch.privilege.xml;
import static li.strolch.privilege.helper.XmlConstants.*;
import java.text.MessageFormat;
import java.util.*;
import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.PasswordCrypt;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.helper.StringHelper;
import li.strolch.utils.iso8601.ISO8601;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,6 +26,11 @@ import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.text.MessageFormat;
import java.util.*;
import static li.strolch.privilege.helper.XmlConstants.*;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
@ -59,8 +58,12 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equals(XML_USER)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(UserParser.class)))
throw new IllegalArgumentException("Previous User not closed!");
this.buildersStack.push(new UserParser());
} else if (qName.equals(XML_PROPERTIES)) {
if (this.buildersStack.stream().anyMatch(e -> e.getClass().equals(PropertyParser.class)))
throw new IllegalArgumentException("Previous Properties not closed!");
this.buildersStack.push(new PropertyParser());
}
@ -157,17 +160,15 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
public void endElement(String uri, String localName, String qName) {
switch (qName) {
case XML_FIRSTNAME -> this.firstName = this.text.toString().trim();
case XML_LASTNAME -> this.lastname = this.text.toString().trim();
case XML_STATE -> this.userState = UserState.valueOf(this.text.toString().trim());
case XML_LOCALE -> this.locale = Locale.forLanguageTag(this.text.toString().trim());
case XML_PASSWORD_CHANGE_REQUESTED ->
this.passwordChangeRequested = Boolean.parseBoolean(this.text.toString().trim());
case XML_FIRST_LOGIN -> this.history.setFirstLogin(ISO8601.parseToZdt(this.text.toString().trim()));
case XML_LAST_LOGIN -> this.history.setLastLogin(ISO8601.parseToZdt(this.text.toString().trim()));
case XML_LAST_PASSWORD_CHANGE ->
this.history.setLastPasswordChange(ISO8601.parseToZdt(this.text.toString().trim()));
case XML_ROLE -> this.userRoles.add(this.text.toString().trim());
case XML_FIRSTNAME -> this.firstName = getText();
case XML_LASTNAME -> this.lastname = getText();
case XML_STATE -> this.userState = UserState.valueOf(getText());
case XML_LOCALE -> this.locale = Locale.forLanguageTag(getText());
case XML_PASSWORD_CHANGE_REQUESTED -> this.passwordChangeRequested = Boolean.parseBoolean(getText());
case XML_FIRST_LOGIN -> this.history.setFirstLogin(ISO8601.parseToZdt(getText()));
case XML_LAST_LOGIN -> this.history.setLastLogin(ISO8601.parseToZdt(getText()));
case XML_LAST_PASSWORD_CHANGE -> this.history.setLastPasswordChange(ISO8601.parseToZdt(getText()));
case XML_ROLE -> this.userRoles.add(getText());
case XML_USER -> {
if (this.history == null)
this.history = new UserHistory();
@ -191,6 +192,10 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
}
}
private String getText() {
return this.text.toString().trim();
}
@Override
public void notifyChild(ElementParser child) {
if (child instanceof PropertyParser) {

View File

@ -0,0 +1,144 @@
/*
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package li.strolch.privilege.xml;
import javanet.staxutils.IndentingXMLStreamWriter;
import li.strolch.privilege.model.internal.PasswordCrypt;
import li.strolch.privilege.model.internal.User;
import li.strolch.privilege.model.internal.UserHistory;
import li.strolch.utils.iso8601.ISO8601;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static java.util.Comparator.comparing;
import static li.strolch.privilege.helper.XmlConstants.*;
import static li.strolch.privilege.helper.XmlHelper.*;
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
import static li.strolch.utils.helper.StringHelper.toHexString;
/**
* @author Robert von Burg <eitch@eitchnet.ch>
*/
public class PrivilegeUsersSaxWriter {
private final List<User> users;
private final File modelFile;
public PrivilegeUsersSaxWriter(List<User> users, File modelFile) {
this.users = users;
this.modelFile = modelFile;
this.users.sort(comparing(User::getUsername));
}
public void write() throws IOException, XMLStreamException {
try (Writer ioWriter = new OutputStreamWriter(new FileOutputStream(this.modelFile), StandardCharsets.UTF_8)) {
IndentingXMLStreamWriter xmlWriter = openXmlStreamWriterDocument(ioWriter);
xmlWriter.writeStartElement(XML_USERS);
List<User> users = new ArrayList<>(this.users);
users.sort(comparing(u -> u.getUsername().toLowerCase(Locale.ROOT)));
for (User user : this.users) {
// start the user element
xmlWriter.writeStartElement(XML_USER);
xmlWriter.writeAttribute(XML_ATTR_USER_ID, user.getUserId());
xmlWriter.writeAttribute(XML_ATTR_USERNAME, user.getUsername());
writePassword(user, xmlWriter);
// add first name element
if (isNotEmpty(user.getFirstname()))
writeStringElement(xmlWriter, XML_FIRSTNAME, user.getFirstname());
// add last name element
if (isNotEmpty(user.getLastname()))
writeStringElement(xmlWriter, XML_LASTNAME, user.getLastname());
// add state element
writeStringElement(xmlWriter, XML_STATE, user.getUserState().toString());
// add locale element
writeStringElement(xmlWriter, XML_LOCALE, user.getLocale().toLanguageTag());
// add password change requested element
if (user.isPasswordChangeRequested())
writeStringElement(xmlWriter, XML_PASSWORD_CHANGE_REQUESTED, "true");
// add all the role elements
if (!user.getRoles().isEmpty()) {
xmlWriter.writeStartElement(XML_ROLES);
writeStringList(xmlWriter, XML_ROLE, user.getRoles());
xmlWriter.writeEndElement();
}
// add the parameters
Map<String, String> properties = user.getProperties();
if (!properties.isEmpty()) {
writeStringMapElement(xmlWriter, properties, XML_PROPERTIES, XML_PROPERTY);
}
if (!user.isHistoryEmpty()) {
UserHistory history = user.getHistory();
xmlWriter.writeStartElement(XML_HISTORY);
if (!history.isFirstLoginEmpty())
writeStringElement(xmlWriter, XML_FIRST_LOGIN, ISO8601.toString(history.getFirstLogin()));
if (!history.isLastLoginEmpty())
writeStringElement(xmlWriter, XML_LAST_LOGIN, ISO8601.toString(history.getLastLogin()));
if (!history.isLastPasswordChangeEmpty())
writeStringElement(xmlWriter, XML_LAST_PASSWORD_CHANGE,
ISO8601.toString(history.getLastPasswordChange()));
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
// and now end
xmlWriter.writeEndDocument();
xmlWriter.flush();
}
}
private void writePassword(User user, XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
PasswordCrypt passwordCrypt = user.getPasswordCrypt();
if (passwordCrypt == null)
return;
String passwordString = passwordCrypt.buildPasswordString();
if (passwordString != null) {
xmlStreamWriter.writeAttribute(XML_ATTR_PASSWORD, passwordString);
} else {
if (passwordCrypt.getPassword() != null)
xmlStreamWriter.writeAttribute(XML_ATTR_PASSWORD, toHexString(passwordCrypt.getPassword()));
if (passwordCrypt.getSalt() != null)
xmlStreamWriter.writeAttribute(XML_ATTR_SALT, toHexString(passwordCrypt.getSalt()));
}
}
}

View File

@ -4,14 +4,17 @@ import static li.strolch.privilege.test.XmlTest.SRC_TEST;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import li.strolch.privilege.helper.WriteRolesFileHelper;
import org.junit.Test;
import javax.xml.stream.XMLStreamException;
public class WriteRolesFileHelperTest {
@Test
public void shouldReadAndWriteRolesFile() {
public void shouldReadAndWriteRolesFile() throws XMLStreamException, IOException {
String src = SRC_TEST + "PrivilegeRoles.xml";
String dst = "target/WriteRolesFileHelperTest_roles.xml";
@ -19,7 +22,7 @@ public class WriteRolesFileHelperTest {
if (new File(dst).exists() && !new File(dst).delete())
throw new IllegalStateException("Could not delete file " + dst);
WriteRolesFileHelper.main(new String[] { src, dst });
WriteRolesFileHelper.main(new String[]{src, dst});
assertTrue(new File(dst).exists());
}

View File

@ -15,10 +15,7 @@
*/
package li.strolch.privilege.test;
import li.strolch.privilege.handler.DefaultEncryptionHandler;
import li.strolch.privilege.handler.MailUserChallengeHandler;
import li.strolch.privilege.handler.PrivilegeHandler;
import li.strolch.privilege.handler.XmlPersistenceHandler;
import li.strolch.privilege.handler.*;
import li.strolch.privilege.model.IPrivilege;
import li.strolch.privilege.model.UserState;
import li.strolch.privilege.model.internal.*;
@ -33,7 +30,10 @@ import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
@ -115,7 +115,7 @@ public class XmlTest {
}
@Test
public void canWriteConfig() {
public void canWriteConfig() throws XMLStreamException, IOException {
Map<String, String> parameterMap = new HashMap<>();
Map<String, String> encryptionHandlerParameterMap = new HashMap<>();
@ -128,6 +128,7 @@ public class XmlTest {
PrivilegeContainerModel containerModel = new PrivilegeContainerModel();
containerModel.setParameterMap(parameterMap);
containerModel.setPrivilegeHandlerClassName(DefaultPrivilegeHandler.class.getName());
containerModel.setEncryptionHandlerClassName(DefaultEncryptionHandler.class.getName());
containerModel.setEncryptionHandlerParameterMap(encryptionHandlerParameterMap);
containerModel.setPersistenceHandlerClassName(XmlPersistenceHandler.class.getName());
@ -138,11 +139,38 @@ public class XmlTest {
containerModel.addPolicy("DefaultPrivilege", "li.strolch.privilege.policy.DefaultPrivilege");
File configFile = new File(TARGET_TEST + "PrivilegeTest.xml");
PrivilegeConfigDomWriter configSaxWriter = new PrivilegeConfigDomWriter(containerModel, configFile);
PrivilegeConfigSaxWriter configSaxWriter = new PrivilegeConfigSaxWriter(containerModel, configFile);
configSaxWriter.write();
String fileHash = StringHelper.toHexString(FileHelper.hashFileSha256(configFile));
assertEquals("dcb6b3ed7198e0a7c88bf5c61c0bd6f0d684415f2a2f29429879edc6bc795f06", fileHash);
String expected = """
<?xml version="1.0" encoding="UTF-8"?>
<Privilege>
<Container>
<Parameters>
<Parameter name="autoPersistOnPasswordChange" value="true"/>
</Parameters>
<PrivilegeHandler class="li.strolch.privilege.handler.DefaultPrivilegeHandler"/>
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
<Parameters>
<Parameter name="hashAlgorithm" value="SHA-256"/>
</Parameters>
</EncryptionHandler>
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler">
<Parameters>
<Parameter name="basePath" value="target/test/"/>
<Parameter name="modelXmlFile" value="PrivilegeModel.xml"/>
</Parameters>
</PersistenceHandler>
<UserChallengeHandler class="li.strolch.privilege.handler.MailUserChallengeHandler"/>
<SsoHandler class="li.strolch.privilege.test.model.DummySsoHandler"/>
</Container>
<Policies>
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege"/>
</Policies>
</Privilege>
""";
assertEquals(expected, Files.readString(configFile.toPath()));
}
@Test
@ -295,7 +323,7 @@ public class XmlTest {
}
@Test
public void canWriteUsers() {
public void canWriteUsers() throws XMLStreamException, IOException {
Map<String, String> propertyMap;
Set<String> userRoles;
@ -324,7 +352,7 @@ public class XmlTest {
users.add(user2);
File modelFile = new File(TARGET_TEST + "PrivilegeUsersTest.xml");
PrivilegeUsersDomWriter configSaxWriter = new PrivilegeUsersDomWriter(users, modelFile);
PrivilegeUsersSaxWriter configSaxWriter = new PrivilegeUsersSaxWriter(users, modelFile);
configSaxWriter.write();
PrivilegeUsersSaxReader xmlHandler = new PrivilegeUsersSaxReader(true);
@ -361,7 +389,7 @@ public class XmlTest {
}
@Test
public void canWriteRoles() {
public void canWriteRoles() throws XMLStreamException, IOException {
Map<String, IPrivilege> privilegeMap;
List<Role> roles = new ArrayList<>();
@ -381,8 +409,8 @@ public class XmlTest {
roles.add(role2);
File modelFile = new File(TARGET_TEST + "PrivilegeRolesTest.xml");
PrivilegeRolesDomWriter configSaxWriter = new PrivilegeRolesDomWriter(roles, modelFile);
configSaxWriter.write();
PrivilegeRolesSaxWriter writer = new PrivilegeRolesSaxWriter(roles, modelFile);
writer.write();
PrivilegeRolesSaxReader xmlHandler = new PrivilegeRolesSaxReader();
XmlHelper.parseDocument(modelFile, xmlHandler);