Commit 9bcbdcf0 authored by Matija Obreza's avatar Matija Obreza

Refactored for updated app-blocks ACL security

- User autocompleter returns IDs and emails
- Use jspHelper.aclSidById() instead of jspHelper.userFullName() for "last modified XX by YY"
parent d8c29ae5
......@@ -46,6 +46,7 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang.StringUtils;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.model.elastic.AccessionDetails;
import org.genesys2.server.model.genesys.Accession;
......@@ -121,7 +122,6 @@ import org.springframework.data.repository.query.Param;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
......@@ -1043,9 +1043,9 @@ public class GenesysServiceImpl implements GenesysService, DatasetService {
@Override
@PreAuthorize("isAuthenticated()")
public List<Metadata> listMyMetadata() {
final UserDetails user = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listIdentitiesForSid(Metadata.class, user, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), user.getUsername());
final AclSid sid = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listObjectIdentityIdsForSid(Metadata.class, sid, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), sid);
if (oids.size() == 0) {
return null;
}
......
......@@ -25,6 +25,7 @@ import java.util.Map;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang.StringUtils;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.model.impl.Country;
import org.genesys2.server.model.impl.FaoInstitute;
......@@ -49,7 +50,6 @@ import org.springframework.data.domain.Sort.Direction;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -240,9 +240,9 @@ public class InstituteServiceImpl implements InstituteService {
@Override
@PreAuthorize("isAuthenticated()")
public List<FaoInstitute> listMyInstitutes(Sort sort) {
final UserDetails user = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listIdentitiesForSid(FaoInstitute.class, user, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), user.getUsername());
final AclSid sid = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listObjectIdentityIdsForSid(FaoInstitute.class, sid, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), sid);
if (oids.size() == 0) {
return null;
}
......
......@@ -23,6 +23,7 @@ import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.model.genesys.AccessionId;
import org.genesys2.server.model.genesys.Metadata;
......@@ -50,7 +51,6 @@ import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -273,9 +273,9 @@ public class TraitServiceImpl implements TraitService {
@Override
@PreAuthorize("isAuthenticated()")
public List<Method> listMyMethods() {
final UserDetails user = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listIdentitiesForSid(Method.class, user, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), user.getUsername());
final AclSid sid = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listObjectIdentityIdsForSid(Method.class, sid, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), sid);
if (oids.size() == 0) {
return null;
}
......
/**
* Copyright 2015 Global Crop Diversity Trust
/*
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -12,7 +12,7 @@
* 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.genesys2.server.servlet.controller;
......@@ -25,9 +25,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.genesys.blocks.oauth.model.OAuthClient;
import org.genesys.blocks.oauth.service.OAuthClientDetailsService;
import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.model.impl.Country;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.User;
......@@ -48,6 +47,9 @@ public class JspHelper {
@Autowired
private UserService userService;
@Autowired
private CustomAclService aclService;
@Autowired
private GeoService geoService;
......@@ -60,9 +62,6 @@ public class JspHelper {
@Autowired
private HtmlConverter htmlConverter;
@Autowired
private OAuthClientDetailsService clientDetailsService;
public String userFullName(Long userId) {
if (userId == null) {
return null;
......@@ -83,11 +82,11 @@ public class JspHelper {
return userService.getUserByUuid(uuid);
}
public OAuthClient getByClientId(final String clientId) {
if (clientId == null) {
public AclSid aclSidById(Long id) {
if (id == null) {
return null;
}
return clientDetailsService.getClient(clientId);
return aclService.getSid(id);
}
public Country getCountry(String iso3) {
......
......@@ -16,14 +16,13 @@
package org.genesys2.server.servlet.controller.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.genesys.blocks.oauth.model.OAuthClient;
import org.genesys.blocks.oauth.service.OAuthClientDetailsService;
import org.genesys.blocks.security.model.AclObjectIdentity;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.UserService;
......@@ -56,29 +55,44 @@ public class PermissionController extends RestController {
@RequestMapping(value = "/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody Object addPermission(@RequestBody PermissionJson permissionJson) {
LOG.info("Adding permission {}", permissionJson);
final Map<Integer, Boolean> permissionMap = PermissionJsonUtil.createPermissionsMap(permissionJson);
aclService.addPermissions(permissionJson.getOid(), permissionJson.getClazz(), permissionJson.getUuid(), permissionJson.isPrincipal(), permissionMap);
if (permissionJson.getAuthority() != null) {
final AclSid sid = aclService.getAuthoritySid(permissionJson.getAuthority());
aclService.addPermissions(permissionJson.getOid(), permissionJson.getClazz(), sid, permissionMap);
} else if (permissionJson.getSid() != null) {
final AclSid sid = aclService.getSid(permissionJson.getSid());
aclService.addPermissions(permissionJson.getOid(), permissionJson.getClazz(), sid, permissionMap);
}
return JSON_OK;
}
@RequestMapping(value = "/update", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody Object updatePermissions(@RequestBody PermissionJson permissionJson) {
final Map<Integer, Boolean> permissionMap = PermissionJsonUtil.createPermissionsMap(permissionJson);
final AclObjectIdentity objectIdentity = aclService.ensureObjectIdentity(permissionJson.getClazz(), permissionJson.getOid());
aclService.updatePermission(objectIdentity, permissionJson.getUuid(), permissionMap);
if (permissionJson.getAuthority() != null) {
final AclSid sid = aclService.getAuthoritySid(permissionJson.getAuthority());
aclService.updatePermissions(objectIdentity, sid, permissionMap);
} else if (permissionJson.getSid() != null) {
final AclSid sid = aclService.getSid(permissionJson.getSid());
aclService.updatePermissions(objectIdentity, sid, permissionMap);
}
return JSON_OK;
}
@RequestMapping(value = "/autocompleteuser", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody List<String> acUser(@RequestParam("term") String email) {
List<String> userEmails = new ArrayList<String>();
public @ResponseBody Map<Long, String> acUser(@RequestParam("term") String email) {
final Map<Long, String> userIds = new HashMap<>();
for (User user : userService.autocompleteUser(email)) {
userEmails.add(user.getEmail());
userIds.put(user.getId(), user.getEmail());
}
return userEmails;
return userIds;
}
@RequestMapping(value = "/autocomplete-oauth-client", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
......
......@@ -17,10 +17,15 @@
package org.genesys2.server.servlet.model;
public class PermissionJson {
// sid
private Long sid;
private String authority;
// object identity
private long oid;
private String clazz;
private String uuid;
private boolean principal;
// permissions
private boolean create;
private boolean read;
private boolean write;
......@@ -29,39 +34,39 @@ public class PermissionJson {
@Override
public String toString() {
return "PJ oid=" + oid + " class=" + clazz + " uuid=" + uuid + " principal=" + principal;
return "PJ oid=" + oid + " class=" + clazz + " (sid=" + sid + " OR authority=" + authority + ")";
}
public long getOid() {
return oid;
public void setSid(Long sid) {
this.sid = sid;
}
public void setOid(long oid) {
this.oid = oid;
public Long getSid() {
return sid;
}
public String getClazz() {
return clazz;
public void setAuthority(String authority) {
this.authority = authority;
}
public void setClazz(String clazz) {
this.clazz = clazz;
public String getAuthority() {
return authority;
}
public String getUuid() {
return uuid;
public long getOid() {
return oid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
public void setOid(long oid) {
this.oid = oid;
}
public boolean isPrincipal() {
return principal;
public String getClazz() {
return clazz;
}
public void setPrincipal(boolean principal) {
this.principal = principal;
public void setClazz(String clazz) {
this.clazz = clazz;
}
public boolean isCreate() {
......
......@@ -1064,7 +1064,7 @@
<div class="section-inner-content clearfix">
<p>
<c:if test="${accession.lastModifiedBy ne null}">
<spring:message code="audit.lastModifiedBy" arguments="${jspHelper.userFullName(accession.lastModifiedBy)}"/>
<spring:message code="audit.lastModifiedBy" arguments="${jspHelper.aclSidById(accession.lastModifiedBy).fullName}"/>
</c:if>
<local:prettyTime date="${accession.lastModifiedDate}" locale="${pageContext.response.locale}"/>
</p>
......
......@@ -15,7 +15,7 @@
<p>
<spring:message code="acl.owner"/>:
<c:out value="${jspHelper.userByUuid(aclObjectIdentity.ownerSid.sid).email}"/>
<c:out value="${jspHelper.aclSidById(aclObjectIdentity.ownerSid.id).fullName}"/>
</p>
<table class="table table-striped">
<thead>
......@@ -31,25 +31,10 @@
<c:forEach items="${aclSids}" var="aclSid" varStatus="status">
<tr class="${status.count % 2 == 0 ? 'even' : 'odd'}">
<td>
<c:choose>
<c:when test="${aclSid.sid.contains('@')}">
<c:out value="${jspHelper.getByClientId(aclSid.sid).title}"/>
</c:when>
<c:when test="${aclSid.principal == false}">
<c:forEach var="role" items="${roles}">
<c:if test="${role.name eq aclSid.sid}">
<c:out value="${aclSid.sid}"/>
</c:if>
</c:forEach>
</c:when>
<c:when test="${aclSid.principal == true}">
<c:out value="${jspHelper.userByUuid(aclSid.sid).email}"/>
</c:when>
</c:choose>
<c:out value="${jspHelper.aclSidById(aclSid.id).fullName}"/>
</td>
<input type="hidden" name="aclSid" class="aclSid" value="${aclSid.sid}"/>
<input type="hidden" name="aclPrincipal" class="aclPrincipal" value="${aclSid.principal}"/>
<input type="hidden" name="sid" class="aclSid" value="${aclSid.id}"/>
<c:forEach items="${aclPermissions}" var="aclPermission">
<td><input type="checkbox" value="1" class="check" name="permissionValue${aclPermission.mask}" id="permissionValue${aclPermission.mask}" disabled="disabled" ${aclEntries[aclSid.sid][aclPermission.mask] ? 'checked' : '' }/></td>
......@@ -112,7 +97,7 @@
<content tag="javascript">
<script type="text/javascript">
jQuery(document).ready(function() {
var oAuthClientMap;
var oAuthClientMap, userMap;
if ($('#permissionAdderByRole select')[0].value == 'SELECT ROLE') {
$("#permissionAdderByRole input[type=button]").prop('disabled', true);
......@@ -129,8 +114,7 @@
var object = {
"oid": ${aclObjectIdentity.objectIdIdentity},
"clazz": "${aclObjectIdentity.aclClass.aclClass}",
"uuid": $('#permissionAdderByRole select')[0].value,
"principal": false,
"authority": $('#permissionAdderByRole select')[0].value,
"create": $("#rAutoCheck4").is(':checked'),
"read": $("#rAutoCheck1").is(':checked'),
"write": $("#rAutoCheck2").is(':checked'),
......@@ -162,8 +146,7 @@
var object = {
"oid": ${aclObjectIdentity.objectIdIdentity},
"clazz": "${aclObjectIdentity.aclClass.aclClass}",
"uuid": oAuthClientMap[$("#permissionAdderByOAuthClient input[type=text]")[0].value],
"principal": true,
"sid": oAuthClientMap[$("#permissionAdderByOAuthClient input[type=text]")[0].value],
"create": $("#oauthAutoCheck4").is(':checked'),
"read": $("#oauthAutoCheck1").is(':checked'),
"write": $("#oauthAutoCheck2").is(':checked'),
......@@ -201,8 +184,7 @@
var object = {
"oid": ${aclObjectIdentity.objectIdIdentity},
"clazz": "${aclObjectIdentity.aclClass.aclClass}",
"uuid": $("#permissionAdder input[type=text]")[0].value,
"principal": true,
"sid": userMap[$("#permissionAdder input[type=text]")[0].value],
"create": create,
"read": read,
"write": write,
......@@ -235,13 +217,12 @@
var write=$(this).parent().parent().find('#permissionValue2').is(':checked');
var remove=$(this).parent().parent().find('#permissionValue8').is(':checked');
var manage=$(this).parent().parent().find('#permissionValue16').is(':checked');
var uuid=$(this).parent().parent().find('.aclSid').val();
var sid=$(this).parent().parent().find('.aclSid').val();
var object = {
"oid": ${aclObjectIdentity.objectIdIdentity},
"clazz": "${aclObjectIdentity.aclClass.aclClass}",
"uuid": uuid,
"principal": true,
"sid": sid,
"create": create,
"read": read,
"write": write,
......@@ -291,7 +272,27 @@
$("#autocomplete-email").autocomplete({
delay: 200,
minLength: 4,
source: "<c:url value='/json/v0/permission/autocompleteuser' />",
source: function(request, response) {
$.ajax("<c:url value='/json/v0/permission/autocompleteuser' />",{
type: "GET",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
"term": $("#permissionAdder input[type=text]")[0].value
},
success: function (data) {
var titles = [];
$.each(data, function(key, element) {
titles.push(key);
});
userMap = data;
response(titles);
},
error: function(result) {
alert("Error");
}
})
},
messages: {
noResults: '',
results: function() {}
......
......@@ -36,10 +36,10 @@
<author>
<c:choose>
<c:when test="${news.lastModifiedBy ne null}">
<name><c:out value="${jspHelper.userFullName(news.lastModifiedBy)}" /></name>
<name><c:out value="${jspHelper.aclSidById(news.lastModifiedBy).fullName}" /></name>
</c:when>
<c:when test="news.createdBy ne null">
<name><c:out value="${jspHelper.userFullName(news.createdBy)}" /></name>
<name><c:out value="${jspHelper.aclSidById(news.createdBy).fullName}" /></name>
</c:when>
<c:otherwise>
<name><spring:message code="page.home.title" /></name>
......
......@@ -92,7 +92,7 @@
</c:if>
<div class="audit-info">
<c:if test="${metadata.lastModifiedBy ne null}"><spring:message code="audit.lastModifiedBy" arguments="${jspHelper.userFullName(metadata.lastModifiedBy)}" /></c:if>
<c:if test="${metadata.lastModifiedBy ne null}"><spring:message code="audit.lastModifiedBy" arguments="${jspHelper.aclSidById(metadata.lastModifiedBy).fullName}" /></c:if>
<fmt:formatDate value="${metadata.lastModifiedDate}" type="both" />
</div>
</c:if>
......
......@@ -48,7 +48,7 @@
</gui:alert>
<div class="audit-info">
<c:if test="${team.lastModifiedBy ne null}"><spring:message code="audit.lastModifiedBy" arguments="${jspHelper.userFullName(team.lastModifiedBy)}" /></c:if>
<c:if test="${team.lastModifiedBy ne null}"><spring:message code="audit.lastModifiedBy" arguments="${jspHelper.aclSidById(team.lastModifiedBy).fullName}" /></c:if>
<fmt:formatDate value="${team.lastModifiedDate}" type="both" />
</div>
</body>
......
......@@ -7,6 +7,7 @@ import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.genesys.blocks.security.SecurityContextUtil;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys2.server.model.genesys.AccessionId;
import org.genesys2.server.model.genesys.Metadata;
......@@ -32,7 +33,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
......@@ -257,9 +257,9 @@ public class TraitServiceMock implements TraitService{
@Override
@PreAuthorize("isAuthenticated()")
public List<Method> listMyMethods() {
final UserDetails user = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listIdentitiesForSid(Method.class, user, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), user.getUsername());
final AclSid sid = SecurityContextUtil.getCurrentUser();
final List<Long> oids = aclService.listObjectIdentityIdsForSid(Method.class, sid, BasePermission.WRITE);
LOG.info("Got {} elements for {}", oids.size(), sid);
if (oids.size() == 0) {
return null;
}
......
......@@ -16,7 +16,6 @@ import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.model.BasicUser.AccountType;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.model.impl.Team;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.servlet.model.PermissionJson;
import org.junit.After;
......@@ -44,7 +43,6 @@ public class PermissionControllerTest extends AbstractRestTest {
private User user = new User();
private FaoInstitute faoInstitute;
private Team team;
private List<FaoInstitute> institutes = new ArrayList<>();
private PermissionJson permissionJson;
private JsonNode JSON_OK;
......@@ -74,8 +72,8 @@ public class PermissionControllerTest extends AbstractRestTest {
permissionJson = new PermissionJson();
permissionJson.setOid(faoInstitute.getId());
permissionJson.setClazz(FaoInstitute.class.getName());
permissionJson.setUuid(user.getEmail());
permissionJson.setPrincipal(true);
permissionJson.setSid(user.getId());
permissionJson.setAuthority(null);
permissionJson.setRead(true);
permissionJson.setWrite(true);
permissionJson.setCreate(true);
......
......@@ -19,7 +19,6 @@ import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyExceptio
import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.genesys.Method;
import org.genesys2.server.model.genesys.Parameter;
import org.genesys2.server.model.genesys.ParameterCategory;
import org.genesys2.server.model.impl.Crop;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.servlet.controller.rest.OAuth2Cleanup;
......@@ -53,7 +52,6 @@ public class TraitsControllerTest extends AbstractRestTest {
private Parameter parameter;
private Crop crop;
private ParameterCategory category;
private Method method;
private User user;
......@@ -80,7 +78,7 @@ public class TraitsControllerTest extends AbstractRestTest {
permission.put(8, false);
permission.put(16, true);
aclService.addPermissions(method.getId(), Method.class.getName(), "SYS_ADMIN", true, permission);
aclService.addPermissions(method.getId(), Method.class.getName(), user, permission);
List<GrantedAuthority> authorities = new ArrayList<>();
GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(UserRole.ADMINISTRATOR.getName());
......
......@@ -80,7 +80,7 @@ public class UserControllerTest extends AbstractRestTest {
permission.put(8, false);
permission.put(16, true);
aclService.addPermissions(faoInstitute.getId(), FaoInstitute.class.getName(), "SYS_ADMIN", true, permission);
aclService.addPermissions(faoInstitute.getId(), FaoInstitute.class.getName(), user, permission);
List<GrantedAuthority> authorities = new ArrayList<>();
GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(UserRole.ADMINISTRATOR.getName());
......
......@@ -21,7 +21,6 @@ import org.genesys.blocks.security.model.BasicUser.AccountType;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys2.server.model.UserRole;
import org.genesys2.server.model.impl.FaoInstitute;
import org.genesys2.server.model.impl.Team;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.CRMException;
import org.genesys2.server.service.ContentService;
......@@ -56,7 +55,6 @@ public class UsersControllerTest extends AbstractRestTest {
private User user = new User();
private FaoInstitute faoInstitute;
private Team team;
private List<FaoInstitute> institutes = new ArrayList<>();
@Before
......@@ -87,7 +85,7 @@ public class UsersControllerTest extends AbstractRestTest {
permission.put(8, false);
permission.put(16, true);
aclService.addPermissions(faoInstitute.getId(), FaoInstitute.class.getName(), "SYS_ADMIN", true, permission);
aclService.addPermissions(faoInstitute.getId(), FaoInstitute.class.getName(), user, permission);
List<GrantedAuthority> authorities = new ArrayList<>();
GrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(UserRole.USER.getName());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment