Implemented permission authorizing module

revert-70aa11f8
milanperera 9 years ago
commit 126c2ec371

@ -71,6 +71,7 @@
org.bouncycastle.operator.jcajce,
org.bouncycastle.pkcs,
org.bouncycastle.util,
org.bouncycastle.asn1.util,
org.jscep.message,
org.jscep.transaction,
org.w3c.dom,

@ -20,7 +20,11 @@ package org.wso2.carbon.certificate.mgt.core.impl;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.Attribute;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extension;
@ -366,6 +370,16 @@ public class CertificateGenerator {
try {
certificateBuilder.addExtension(X509Extension.keyUsage, true, new KeyUsage(
KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
if(attributes != null) {
ASN1Encodable extractedValue = getChallengePassword(attributes);
if(extractedValue != null) {
certificateBuilder.addExtension(PKCSObjectIdentifiers.pkcs_9_at_challengePassword, true,
extractedValue);
}
}
sigGen = new JcaContentSignerBuilder(ConfigurationUtil.SHA256_RSA)
.setProvider(ConfigurationUtil.PROVIDER).build(privateKey);
issuedCert = new JcaX509CertificateConverter().setProvider(
@ -390,6 +404,19 @@ public class CertificateGenerator {
return issuedCert;
}
private ASN1Encodable getChallengePassword(Attribute[] attributes) {
for (Attribute attribute : attributes) {
if (PKCSObjectIdentifiers.pkcs_9_at_challengePassword.equals(attribute.getAttrType())) {
if(attribute.getAttrValues() != null && attribute.getAttrValues().size() > 0) {
return attribute.getAttrValues().getObjectAt(0);
}
}
}
return null;
}
private CMSSignedData getMessageData(final List<X509Certificate> certs) throws KeystoreException {
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
@ -534,4 +561,39 @@ public class CertificateGenerator {
throw new KeystoreException(errorMsg, e);
}
}
public String extractChallengeToken(X509Certificate certificate) {
byte[] challengePassword = certificate.getExtensionValue(
PKCSObjectIdentifiers.pkcs_9_at_challengePassword.toString());
if (challengePassword != null) {
return new String(challengePassword);
}
return null;
}
private ASN1Primitive toASN1Primitive(byte[] data) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
ASN1InputStream inputStream = new ASN1InputStream(byteArrayInputStream);
try {
return inputStream.readObject();
} catch (IOException e) {
String errorMsg = "IOException occurred when converting binary array to ASN1Primitive";
log.error(errorMsg, e);
} finally {
try {
byteArrayInputStream.close();
inputStream.close();
} catch (IOException e) {
String errorMsg = "IOException occurred when closing streams";
log.error(errorMsg, e);
}
}
return null;
}
}

@ -51,4 +51,6 @@ public interface CertificateManagementService {
boolean verifySignature(String headerSignature) throws KeystoreException;
public X509Certificate extractCertificateFromSignature(String headerSignature) throws KeystoreException;
String extractChallengeToken(X509Certificate certificate);
}

@ -96,4 +96,8 @@ public class CertificateManagementServiceImpl implements CertificateManagementSe
public X509Certificate extractCertificateFromSignature(String headerSignature) throws KeystoreException {
return certificateGenerator.extractCertificateFromSignature(headerSignature);
}
public String extractChallengeToken(X509Certificate certificate) {
return certificateGenerator.extractChallengeToken(certificate);
}
}

@ -0,0 +1,38 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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 org.wso2.carbon.device.mgt.core.config.permission;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
@XmlRootElement(name = "PermissionConfiguration")
public class PermissionConfiguration {
private List<Permission> permissions;
public List<Permission> getPermissions() {
return permissions;
}
@XmlElement(name = "Permission", required = true)
public void setPermissions(List<Permission> permissions) {
this.permissions = permissions;
}
}

@ -0,0 +1,77 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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 org.wso2.carbon.device.mgt.core.config.permission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class represents the node of a permission graph.
*/
public class PermissionHolder {
String pathName;
Map<String, Permission> permissions = new HashMap<String, Permission>();
List<PermissionHolder> children = new ArrayList<PermissionHolder>();
public PermissionHolder(String pathName) {
this.pathName = pathName;
}
public String getPathName() {
return pathName;
}
public void setPathName(String pathName) {
this.pathName = pathName;
}
public List<PermissionHolder> getChildren() {
return children;
}
public PermissionHolder getChild(String pathName) {
PermissionHolder child = null;
for (PermissionHolder node : children) {
if (node.getPathName().equals(pathName)) {
return node;
}
}
return child;
}
public void addChild(PermissionHolder node) {
children.add(node);
}
public void addPermission(String httpMethod, Permission permission) {
permissions.put(httpMethod, permission);
}
public Permission getPermission(String httpMethod) {
return permissions.get(httpMethod);
}
public Collection<Permission> getPermissions() {
return permissions.values();
}
}

@ -142,7 +142,7 @@ public class DeviceDAOImpl implements DeviceDAO {
HashMap<Integer, Device> deviceHashMap = new HashMap<>();
try {
conn = this.getConnection();
String sql = "SELECT d1.ID AS DEVICE_ID, d1.DESCRIPTION, d1.NAME AS DEVICE_NAME, d1.DEVICE_TYPE, d1.TENANT_ID, " +
String sql = "SELECT d1.ID AS DEVICE_ID, d1.DESCRIPTION, d1.NAME AS DEVICE_NAME, d1.DEVICE_TYPE, e.TENANT_ID, " +
"d1.DEVICE_IDENTIFICATION, e.OWNER, e.OWNERSHIP, e.STATUS, e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, e.ID AS ENROLMENT_ID FROM DM_ENROLMENT e, (SELECT d.ID, d.DESCRIPTION, d.NAME, " +
"t.NAME AS DEVICE_TYPE, d.DEVICE_IDENTIFICATION FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE " +

@ -0,0 +1,43 @@
package org.wso2.carbon.device.mgt.core.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.device.mgt.core.scep.SCEPManager;
import org.wso2.carbon.device.mgt.core.scep.SCEPManagerImpl;
/**
* @scr.component name="org.wso2.carbon.device.mgt.core.scep" immediate="true"
*/
public class SCEPManagerServiceComponent {
private static final Log log = LogFactory.getLog(SCEPManagerServiceComponent.class);
protected void activate(ComponentContext componentContext) {
try {
if (log.isDebugEnabled()) {
log.debug("Initializing SCEP core bundle");
}
BundleContext bundleContext = componentContext.getBundleContext();
bundleContext.registerService(SCEPManager.class.getName(),
new SCEPManagerImpl(), null);
if (log.isDebugEnabled()) {
log.debug("SCEP core bundle has been successfully initialized");
}
} catch (Throwable e) {
String msg = "Error occurred while initializing SCEP core bundle";
log.error(msg, e);
}
}
protected void deactivate(ComponentContext ctx) {
if (log.isDebugEnabled()) {
log.debug("Deactivating SCEP core bundle");
}
}
}

@ -95,7 +95,9 @@
org.wso2.carbon.apimgt.impl,
org.wso2.carbon.certificate.mgt.core.service,
org.wso2.carbon.certificate.mgt.core.exception,
org.wso2.carbon.device.mgt.core.config.permission
org.wso2.carbon.device.mgt.core.config.permission,
org.wso2.carbon.device.mgt.common,
org.wso2.carbon.device.mgt.core.scep
</Import-Package>
<!--<Fragment-Host>tomcat</Fragment-Host>-->
</instructions>
@ -157,6 +159,10 @@
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.core</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
</dependency>
</dependencies>
</project>

@ -19,6 +19,8 @@
package org.wso2.carbon.webapp.authenticator.framework;
import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService;
import org.wso2.carbon.device.mgt.core.scep.SCEPManager;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.user.core.service.RealmService;
public class DataHolder {
@ -26,11 +28,11 @@ public class DataHolder {
private WebappAuthenticatorRepository repository;
private RealmService realmService;
private CertificateManagementService certificateManagementService;
private SCEPManager scepManager;
private static DataHolder thisInstance = new DataHolder();
private DataHolder() {}
private static DataHolder thisInstance = new DataHolder();
public static DataHolder getInstance() {
return thisInstance;
}
@ -58,4 +60,12 @@ public class DataHolder {
public void setCertificateManagementService(CertificateManagementService certificateManagementService) {
this.certificateManagementService = certificateManagementService;
}
public SCEPManager getScepManager() {
return scepManager;
}
public void setScepManager(SCEPManager scepManager) {
this.scepManager = scepManager;
}
}

@ -27,13 +27,10 @@ import org.wso2.carbon.tomcat.ext.valves.CompositeValve;
import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
public class WebappAuthenticatorFrameworkValve extends CarbonTomcatValve {
private static final String AUTHENTICATION_SCHEME = "authentication-scheme";
private static final String BYPASS_URIS = "bypass-uris";
private static final Log log = LogFactory.getLog(WebappAuthenticatorFrameworkValve.class);
@Override
@ -46,22 +43,6 @@ public class WebappAuthenticatorFrameworkValve extends CarbonTomcatValve {
return;
}
String byPassURIs = request.getContext().findParameter(WebappAuthenticatorFrameworkValve.BYPASS_URIS);
if(byPassURIs != null && !byPassURIs.isEmpty()) {
List<String> requestURI = Arrays.asList(byPassURIs.split(","));
if(requestURI != null && requestURI.size() > 0) {
for (String pathURI : requestURI) {
if (request.getRequestURI().equals(pathURI)) {
this.getNext().invoke(request, response, compositeValve);
return;
}
}
}
}
WebappAuthenticator authenticator = WebappAuthenticatorFactory.getAuthenticator(authScheme);
if (authenticator == null) {
String msg = "Failed to load an appropriate authenticator to authenticate the request";

@ -5,8 +5,16 @@ import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.certificate.mgt.core.exception.KeystoreException;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.core.scep.SCEPException;
import org.wso2.carbon.device.mgt.core.scep.SCEPManager;
import org.wso2.carbon.device.mgt.core.scep.TenantedDeviceWrapper;
import org.wso2.carbon.webapp.authenticator.framework.DataHolder;
import java.security.cert.X509Certificate;
/**
* This authenticator authenticates HTTP requests using certificates.
*/
@ -47,12 +55,35 @@ public class CertificateAuthenticator implements WebappAuthenticator {
if (certHeader != null && DataHolder.getInstance().getCertificateManagementService().
verifySignature(certHeader)) {
return Status.SUCCESS;
X509Certificate certificate = DataHolder.getInstance().getCertificateManagementService().
extractCertificateFromSignature(certHeader);
String challengeToken = DataHolder.getInstance().getCertificateManagementService().
extractChallengeToken(certificate);
if(challengeToken != null) {
challengeToken = challengeToken.substring(challengeToken.indexOf("(") + 1).trim();
SCEPManager scepManager = DataHolder.getInstance().getScepManager();
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
deviceIdentifier.setId(challengeToken);
deviceIdentifier.setType(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS);
TenantedDeviceWrapper tenantedDeviceWrapper = scepManager.getValidatedDevice(deviceIdentifier);
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
ctx.setTenantId(tenantedDeviceWrapper.getTenantId());
ctx.setTenantDomain(tenantedDeviceWrapper.getTenantDomain());
return Status.SUCCESS;
}
}
}
} catch (KeystoreException e) {
log.error("KeystoreException occurred ", e);
return Status.FAILURE;
} catch (SCEPException e) {
log.error("SCEPException occurred ", e);
}
return Status.FAILURE;

@ -28,8 +28,6 @@ import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.util.KeyStoreManager;
import org.wso2.carbon.user.api.TenantManager;
@ -37,13 +35,11 @@ import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import org.wso2.carbon.webapp.authenticator.framework.Constants;
import org.wso2.carbon.webapp.authenticator.framework.DataHolder;
import java.security.interfaces.RSAPublicKey;
import java.text.ParseException;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
/**
* This authenticator authenticates HTTP requests using JWT header.
@ -128,6 +124,11 @@ public class JWTAuthenticator implements WebappAuthenticator {
}
private String decodeAuthorizationHeader(String authorizationHeader) {
if(authorizationHeader == null) {
return null;
}
String[] splitValues = authorizationHeader.trim().split(" ");
byte[] decodedBytes = Base64Utils.decode(splitValues[1].trim());
if (decodedBytes != null) {

@ -0,0 +1,86 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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 org.wso2.carbon.webapp.authenticator.framework.authorizer;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.core.config.permission.Permission;
import org.wso2.carbon.device.mgt.core.config.permission.PermissionManager;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator;
import java.util.StringTokenizer;
/**
* This class represents the methods that are used to authorize requests.
*/
public class PermissionAuthorizer {
private static final Log log = LogFactory.getLog(PermissionAuthorizer.class);
public WebappAuthenticator.Status authorize(Request request, Response response) {
// contextOperation is used to get defined operation type from the web.xml
String requestUri = request.getRequestURI();
String requestMethod = request.getMethod();
if (requestUri == null || requestUri.isEmpty() ||
requestMethod == null || requestMethod.isEmpty()) {
return WebappAuthenticator.Status.CONTINUE;
}
PermissionManager permissionManager = PermissionManager.getInstance();
Permission requestPermission = permissionManager.getPermission(requestUri, requestMethod);
if (requestPermission == null) {
if (log.isDebugEnabled()) {
log.debug("Permission to request '" + requestUri + "' is not defined in the configuration");
}
return WebappAuthenticator.Status.FAILURE;
}
String permissionString = requestPermission.getPath();
// This is added temporarily until authentication works.
String username = "admin";
boolean isUserAuthorized;
try {
isUserAuthorized = CarbonContext.getThreadLocalCarbonContext().getUserRealm().
getAuthorizationManager().isUserAuthorized(username, permissionString, "read");
} catch (UserStoreException e) {
log.error("Error occurred while retrieving user store. " + e.getMessage());
return WebappAuthenticator.Status.FAILURE;
}
if (log.isDebugEnabled()) {
log.debug("Is user authorized: " + isUserAuthorized);
}
if (isUserAuthorized) {
return WebappAuthenticator.Status.SUCCESS;
} else {
return WebappAuthenticator.Status.FAILURE;
}
}
}

@ -0,0 +1,76 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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 org.wso2.carbon.webapp.authenticator.framework.authorizer;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve;
import org.wso2.carbon.tomcat.ext.valves.CompositeValve;
import org.wso2.carbon.webapp.authenticator.framework.AuthenticationFrameworkUtil;
import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator;
import javax.servlet.http.HttpServletResponse;
public class PermissionAuthorizerValve extends CarbonTomcatValve {
private static final Log log = LogFactory.getLog(PermissionAuthorizerValve.class);
private static final String AUTHORIZATION_ENABLED = "authorization-enabled";
@Override
public void invoke(Request request, Response response, CompositeValve compositeValve) {
String permissionStatus =
request.getContext().findParameter(AUTHORIZATION_ENABLED);
if (permissionStatus == null || permissionStatus.isEmpty()) {
this.processResponse(request, response, compositeValve, WebappAuthenticator.Status.CONTINUE);
return;
}
// check whether the permission checking function is enabled
boolean isEnabled = new Boolean(permissionStatus);
if (!isEnabled) {
this.processResponse(request, response, compositeValve, WebappAuthenticator.Status.CONTINUE);
return;
}
if (log.isDebugEnabled()) {
log.debug("Checking permission of request: " + request.getRequestURI());
}
PermissionAuthorizer permissionAuthorizer = new PermissionAuthorizer();
WebappAuthenticator.Status status = permissionAuthorizer.authorize(request, response);
this.processResponse(request, response, compositeValve, status);
}
private void processResponse(Request request, Response response, CompositeValve compositeValve,
WebappAuthenticator.Status status) {
switch (status) {
case SUCCESS:
case CONTINUE:
this.getNext().invoke(request, response, compositeValve);
break;
case FAILURE:
String msg = "Failed to authorize incoming request";
log.error(msg);
AuthenticationFrameworkUtil.handleResponse(request, response, HttpServletResponse.SC_UNAUTHORIZED, msg);
break;
}
}
}

@ -22,6 +22,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.certificate.mgt.core.service.CertificateManagementService;
import org.wso2.carbon.device.mgt.core.scep.SCEPManager;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve;
import org.wso2.carbon.tomcat.ext.valves.TomcatValveContainer;
import org.wso2.carbon.user.core.service.RealmService;
@ -51,6 +53,12 @@ import java.util.List;
* cardinality="1..n"
* bind="setCertificateManagementService"
* unbind="unsetCertificateManagementService"
* @scr.reference name="org.wso2.carbon.device.mgt.core.scep"
* interface="org.wso2.carbon.device.mgt.core.scep.SCEPManager"
* policy="dynamic"
* cardinality="1..n"
* bind="setSCEPManagementService"
* unbind="unsetSCEPManagementService"
*/
public class WebappAuthenticatorFrameworkServiceComponent {
@ -120,4 +128,19 @@ public class WebappAuthenticatorFrameworkServiceComponent {
DataHolder.getInstance().setCertificateManagementService(null);
}
protected void setSCEPManagementService(SCEPManager scepManager) {
if (log.isDebugEnabled()) {
log.debug("Setting SCEP management service");
}
DataHolder.getInstance().setScepManager(scepManager);
}
protected void unsetSCEPManagementService(SCEPManager scepManager) {
if (log.isDebugEnabled()) {
log.debug("Removing SCEP management service");
}
DataHolder.getInstance().setScepManager(null);
}
}

@ -1,3 +1,2 @@
instructions.configure = \
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.webapp.authenticator.framework.server_${feature.version}/conf/webapp-authenticator-config.xml,target:${installFolder}/../../conf/etc/webapp-authenticator-config.xml,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.webapp.authenticator.framework.server_${feature.version}/conf/permissions-config.xml,target:${installFolder}/../../conf/etc/permissions-config.xml,overwrite:true);\
Loading…
Cancel
Save