summaryrefslogtreecommitdiff
path: root/qpid/java/broker-plugins/access-control/src/test
diff options
context:
space:
mode:
authorRobert Gemmell <robbie@apache.org>2012-08-24 15:33:00 +0000
committerRobert Gemmell <robbie@apache.org>2012-08-24 15:33:00 +0000
commit881f4a51e4b9e44f888366fc06f6070f47921b90 (patch)
treec081e6cd763dde58e1f9254cdddbba872be619b6 /qpid/java/broker-plugins/access-control/src/test
parent3196cc6e0e4f6c537ab7f3694d7b2e454cd97fbc (diff)
downloadqpid-python-881f4a51e4b9e44f888366fc06f6070f47921b90.tar.gz
QPID-4236, QPID-4237, QPID-4245: Added group management functionality, and exposed it via the web management interface. Removed group definition functionality from the ACL plugin. The ACL plugin uses groups but no longer defines them.
Introduced SubjectCreator as a facade to AuthenticationManager and group management classes. Applied access control to user and group management. Fixed bug in choosing between update and creation in RestServlet. Moved lots of RestTest helper methods to RestTestHelper. Fixed authentication, authorisation, and status logging when using the web ui. Applied patch from Philip Harvey <phil@philharveyonline.com>, Keith Wall and myself. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1376968 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker-plugins/access-control/src/test')
-rw-r--r--qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java51
-rw-r--r--qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java86
-rw-r--r--qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java92
3 files changed, 138 insertions, 91 deletions
diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java
index 5db02d10ce..2385bcc3dd 100644
--- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java
+++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java
@@ -20,8 +20,6 @@
*/
package org.apache.qpid.server.security.access.plugins;
-import java.util.Arrays;
-
import junit.framework.TestCase;
import org.apache.commons.configuration.ConfigurationException;
@@ -37,19 +35,22 @@ import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.security.access.config.Rule;
import org.apache.qpid.server.security.access.config.RuleSet;
-import org.apache.qpid.server.security.auth.sasl.TestPrincipalUtils;
+import org.apache.qpid.server.security.auth.TestPrincipalUtils;
/**
- * Unit test for ACL V2 plugin.
- *
+ * Unit test for ACL V2 plugin.
+ *
* This unit test tests the AccessControl class and it collaboration with {@link RuleSet},
* {@link SecurityManager} and {@link CurrentActor}. The ruleset is configured programmatically,
* rather than from an external file.
- *
+ *
* @see RuleSetTest
*/
public class AccessControlTest extends TestCase
{
+ private static final String ALLOWED_GROUP = "allowed_group";
+ private static final String DENIED_GROUP = "denied_group";
+
private AccessControl _plugin = null; // Class under test
private final UnitTestMessageLogger messageLogger = new UnitTestMessageLogger();
@@ -68,14 +69,12 @@ public class AccessControlTest extends TestCase
private RuleSet createGroupRuleSet()
{
final RuleSet rs = new RuleSet();
- rs.addGroup("aclGroup1", Arrays.asList(new String[] {"member1", "Member2"}));
// Rule expressed with username
rs.grant(0, "user1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- // Rule expressed with a acl group
- rs.grant(1, "aclGroup1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- // Rule expressed with an external group
- rs.grant(2, "extGroup1", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ // Rules expressed with groups
+ rs.grant(1, ALLOWED_GROUP, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ rs.grant(2, DENIED_GROUP, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
// Catch all rule
rs.grant(3, Rule.ALL, Permission.DENY_LOG, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
@@ -117,31 +116,23 @@ public class AccessControlTest extends TestCase
* Tests that an allow rule expressed with an <b>ACL groupname</b> allows an operation performed by a thread running
* by a user who belongs to the same group..
*/
- public void testAclGroupMembershipAllowsOperation() throws ConfigurationException
+ public void testGroupMembershipAllowsOperation() throws ConfigurationException
{
setUpGroupAccessControl();
- SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("member1"));
- Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- assertEquals(Result.ALLOWED, result);
-
- SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("Member2"));
-
- result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- assertEquals(Result.ALLOWED, result);
+ authoriseAndAssertResult(Result.ALLOWED, "member of allowed group", ALLOWED_GROUP);
+ authoriseAndAssertResult(Result.DENIED, "member of denied group", DENIED_GROUP);
+ authoriseAndAssertResult(Result.ALLOWED, "another member of allowed group", ALLOWED_GROUP);
}
/**
- * Tests that a deny rule expressed with an <b>External groupname</b> denies an operation performed by a thread running
+ * Tests that a deny rule expressed with a <b>groupname</b> denies an operation performed by a thread running
* by a user who belongs to the same group.
*/
- public void testExternalGroupMembershipDeniesOperation() throws ConfigurationException
+ public void testGroupMembershipDeniesOperation() throws ConfigurationException
{
setUpGroupAccessControl();
- SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("user3", "extGroup1"));
-
- final Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- assertEquals(Result.DENIED, result);
+ authoriseAndAssertResult(Result.DENIED, "user3", DENIED_GROUP);
}
/**
@@ -325,6 +316,14 @@ public class AccessControlTest extends TestCase
assertEquals(Result.DEFER, result);
}
+ private void authoriseAndAssertResult(Result expectedResult, String userName, String... groups)
+ {
+ SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject(userName, groups));
+
+ Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ assertEquals(expectedResult, result);
+ }
+
/**
* Creates a configuration plugin for the {@link AccessControl} plugin.
*/
diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java
index c2282694fb..be4962615c 100644
--- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java
+++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java
@@ -108,19 +108,6 @@ public class PlainConfigurationTest extends TestCase
}
}
- public void testACLFileSyntaxNotEnoughGroup() throws Exception
- {
- try
- {
- writeACLConfig("GROUP blah");
- fail("fail");
- }
- catch (ConfigurationException ce)
- {
- assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_GROUP_MSG, 1), ce.getMessage());
- }
- }
-
public void testACLFileSyntaxNotEnoughACL() throws Exception
{
try
@@ -391,4 +378,77 @@ public class PlainConfigurationTest extends TestCase
assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties());
}
+ public void testUserRuleParsing() throws Exception
+ {
+ validateRule(writeACLConfig("ACL ALLOW user1 CREATE USER"),
+ "user1", Operation.CREATE, ObjectType.USER, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 CREATE USER name=\"otherUser\""),
+ "user1", Operation.CREATE, ObjectType.USER, new ObjectProperties("otherUser"));
+
+ validateRule(writeACLConfig("ACL ALLOW user1 DELETE USER"),
+ "user1", Operation.DELETE, ObjectType.USER, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 DELETE USER name=\"otherUser\""),
+ "user1", Operation.DELETE, ObjectType.USER, new ObjectProperties("otherUser"));
+
+ validateRule(writeACLConfig("ACL ALLOW user1 UPDATE USER"),
+ "user1", Operation.UPDATE, ObjectType.USER, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 UPDATE USER name=\"otherUser\""),
+ "user1", Operation.UPDATE, ObjectType.USER, new ObjectProperties("otherUser"));
+
+ validateRule(writeACLConfig("ACL ALLOW user1 ALL USER"),
+ "user1", Operation.ALL, ObjectType.USER, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 ALL USER name=\"otherUser\""),
+ "user1", Operation.ALL, ObjectType.USER, new ObjectProperties("otherUser"));
+ }
+
+ public void testGroupRuleParsing() throws Exception
+ {
+ validateRule(writeACLConfig("ACL ALLOW user1 CREATE GROUP"),
+ "user1", Operation.CREATE, ObjectType.GROUP, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 CREATE GROUP name=\"groupName\""),
+ "user1", Operation.CREATE, ObjectType.GROUP, new ObjectProperties("groupName"));
+
+ validateRule(writeACLConfig("ACL ALLOW user1 DELETE GROUP"),
+ "user1", Operation.DELETE, ObjectType.GROUP, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 DELETE GROUP name=\"groupName\""),
+ "user1", Operation.DELETE, ObjectType.GROUP, new ObjectProperties("groupName"));
+
+ validateRule(writeACLConfig("ACL ALLOW user1 UPDATE GROUP"),
+ "user1", Operation.UPDATE, ObjectType.GROUP, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 UPDATE GROUP name=\"groupName\""),
+ "user1", Operation.UPDATE, ObjectType.GROUP, new ObjectProperties("groupName"));
+
+ validateRule(writeACLConfig("ACL ALLOW user1 ALL GROUP"),
+ "user1", Operation.ALL, ObjectType.GROUP, ObjectProperties.EMPTY);
+ validateRule(writeACLConfig("ACL ALLOW user1 ALL GROUP name=\"groupName\""),
+ "user1", Operation.ALL, ObjectType.GROUP, new ObjectProperties("groupName"));
+ }
+
+ /** explicitly test for exception indicating that this functionality has been moved to Group Providers */
+ public void testGroupDefinitionThrowsException() throws Exception
+ {
+ try
+ {
+ writeACLConfig("GROUP group1 bob alice");
+ fail("Expected exception not thrown");
+ }
+ catch(ConfigurationException e)
+ {
+ assertTrue(e.getMessage().contains("GROUP keyword not supported"));
+ }
+ }
+
+ private void validateRule(final PlainConfiguration config, String username, Operation operation, ObjectType objectType, ObjectProperties objectProperties)
+ {
+ final RuleSet rs = config.getConfiguration();
+ assertEquals(1, rs.getRuleCount());
+
+ final Map<Integer, Rule> rules = rs.getAllRules();
+ assertEquals(1, rules.size());
+ final Rule rule = rules.get(0);
+ assertEquals("Rule has unexpected identity", username, rule.getIdentity());
+ assertEquals("Rule has unexpected operation", operation, rule.getAction().getOperation());
+ assertEquals("Rule has unexpected operation", objectType, rule.getAction().getObjectType());
+ assertEquals("Rule has unexpected object properties", objectProperties, rule.getAction().getProperties());
+ }
}
diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java
index f7cc60543d..181d693614 100644
--- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java
+++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java
@@ -22,7 +22,6 @@
package org.apache.qpid.server.security.access.plugins;
import java.security.Principal;
-import java.util.Arrays;
import javax.security.auth.Subject;
@@ -34,8 +33,7 @@ import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.security.access.config.Rule;
import org.apache.qpid.server.security.access.config.RuleSet;
-import org.apache.qpid.server.security.auth.sasl.TestPrincipalUtils;
-import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
+import org.apache.qpid.server.security.auth.TestPrincipalUtils;
import org.apache.qpid.test.utils.QpidTestCase;
/**
@@ -46,10 +44,7 @@ import org.apache.qpid.test.utils.QpidTestCase;
* access control mechanism is validated by checking whether operations would be authorised by calling the
* {@link RuleSet#check(Principal, Operation, ObjectType, ObjectProperties)} method.
*
- * It ensure that permissions can be granted correctly on users directly, ACL groups (that is those
- * groups declared directly in the ACL itself), and External groups (that is a group from an External
- * Authentication Provider, such as an LDAP).
-
+ * It ensure that permissions can be granted correctly on users directly and on groups.
*/
public class RuleSetTest extends QpidTestCase
{
@@ -316,63 +311,36 @@ public class RuleSetTest extends QpidTestCase
assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
- /**
- * Tests support for ACL groups (i.e. inline groups declared in the ACL file itself).
- */
- public void testAclGroupsSupported()
+ public void testGroupsSupported()
{
- assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera", "userb"})));
-
- _ruleSet.grant(1, "aclgroup", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- assertEquals(1, _ruleSet.getRuleCount());
-
- assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
- assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
- assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("userc"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
- }
-
- /**
- * Tests support for nested ACL groups.
- */
- public void testNestedAclGroupsSupported()
- {
- assertTrue(_ruleSet.addGroup("aclgroup1", Arrays.asList(new String[] {"userb"})));
- assertTrue(_ruleSet.addGroup("aclgroup2", Arrays.asList(new String[] {"usera", "aclgroup1"})));
-
- _ruleSet.grant(1, "aclgroup2", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- assertEquals(1, _ruleSet.getRuleCount());
+ String allowGroup = "allowGroup";
+ String deniedGroup = "deniedGroup";
- assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
- assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
- }
+ _ruleSet.grant(1, allowGroup, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ _ruleSet.grant(2, deniedGroup, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- /**
- * Tests support for nested External groups (i.e. those groups coming from an external source such as an LDAP).
- */
- public void testExternalGroupsSupported()
- {
- _ruleSet.grant(1, "extgroup1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- _ruleSet.grant(2, "extgroup2", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
assertEquals(2, _ruleSet.getRuleCount());
- assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", "extgroup1"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
- assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", "extgroup2"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+ assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", allowGroup),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+ assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", deniedGroup),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+ assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("user", "group not mentioned in acl"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
/**
* Rule order in the ACL determines the outcome of the check. This test ensures that a user who is
- * granted explicit permission on an object, is granted that access even although late a group
+ * granted explicit permission on an object, is granted that access even though a group
* to which the user belongs is later denied the permission.
*/
public void testAllowDeterminedByRuleOrder()
{
- assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"})));
+ String group = "group";
+ String user = "user";
- _ruleSet.grant(1, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- _ruleSet.grant(2, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ _ruleSet.grant(1, user, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ _ruleSet.grant(2, group, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
assertEquals(2, _ruleSet.getRuleCount());
- assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+ assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject(user, group),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
/**
@@ -381,13 +349,33 @@ public class RuleSetTest extends QpidTestCase
*/
public void testDenyDeterminedByRuleOrder()
{
- assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"})));
+ String group = "aclgroup";
+ String user = "usera";
- _ruleSet.grant(1, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
- _ruleSet.grant(2, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ _ruleSet.grant(1, group, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ _ruleSet.grant(2, user, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
assertEquals(2, _ruleSet.getRuleCount());
- assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+ assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject(user, group),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+ }
+
+ public void testUserInMultipleGroups()
+ {
+ String allowedGroup = "group1";
+ String deniedGroup = "group2";
+
+ _ruleSet.grant(1, allowedGroup, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+ _ruleSet.grant(2, deniedGroup, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY);
+
+ Subject subjectInBothGroups = TestPrincipalUtils.createTestSubject("user", allowedGroup, deniedGroup);
+ Subject subjectInDeniedGroupAndOneOther = TestPrincipalUtils.createTestSubject("user", deniedGroup, "some other group");
+ Subject subjectInAllowedGroupAndOneOther = TestPrincipalUtils.createTestSubject("user", allowedGroup, "some other group");
+
+ assertEquals(Result.ALLOWED, _ruleSet.check(subjectInBothGroups,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+
+ assertEquals(Result.DENIED, _ruleSet.check(subjectInDeniedGroupAndOneOther,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
+
+ assertEquals(Result.ALLOWED, _ruleSet.check(subjectInAllowedGroupAndOneOther,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY));
}
}