Added trivial implementation of ACL patterns

This commit is contained in:
andrea
2015-05-26 22:40:34 +02:00
parent 092e30a552
commit db4b7ca192
9 changed files with 73 additions and 42 deletions
@@ -299,7 +299,7 @@ class ProtocolProcessor implements EventHandler<ValueEvent> {
boolean retain = msg.isRetainFlag();
//check if the topic can be wrote
String user = (String) session.getAttribute(NettyChannel.ATTR_KEY_USERNAME);
if (m_authorizator.canWrite(topic, user)) {
if (m_authorizator.canWrite(topic, user, clientID)) {
processPublish(clientID, topic, qos, message, retain, msg.getMessageID());
} else {
LOG.debug("topic {} doesn't have write credentials", topic);
@@ -439,7 +439,7 @@ class ProtocolProcessor implements EventHandler<ValueEvent> {
LOG.debug("Session for clientId {} is {}", clientId, session);
String user = (String) session.getAttribute(NettyChannel.ATTR_KEY_USERNAME);
if (!m_authorizator.canRead(topic, user)) {
if (!m_authorizator.canRead(topic, user, clientId)) {
LOG.debug("topic {} doesn't have read credentials", topic);
return;
}
@@ -29,7 +29,7 @@ public class Authorization {
* */
enum Permission {
READ, WRITE, READWRITE
};
}
Authorization(String topic) {
this(topic, Permission.READWRITE);
@@ -42,6 +42,7 @@ class AuthorizationsCollector implements IAuthorizator {
private List<Authorization> m_patternAuthorizations = new ArrayList();
private Map<String, List<Authorization>> m_userAuthorizations = new HashMap();
private boolean m_parsingUsersSpecificSection = false;
private boolean m_parsingPatternSpecificSection = false;
private String m_currentUser = "";
static final AuthorizationsCollector emptyImmutableCollector() {
@@ -65,6 +66,8 @@ class AuthorizationsCollector implements IAuthorizator {
}
List<Authorization> userAuths = m_userAuthorizations.get(m_currentUser);
userAuths.add(acl);
} else if (m_parsingPatternSpecificSection) {
m_patternAuthorizations.add(acl);
} else {
m_globalAuthorizations.add(acl);
}
@@ -75,43 +78,50 @@ class AuthorizationsCollector implements IAuthorizator {
String keyword = tokens[0].toLowerCase();
switch (keyword) {
case "topic":
if (tokens.length > 2) {
//if the tokenized lines has 3 token the second must be the permission
try {
Authorization.Permission permission = Authorization.Permission.valueOf(tokens[1].toUpperCase());
//bring topic with all original spacing
String topic = line.substring(line.indexOf(tokens[2]));
return new Authorization(topic, permission);
} catch (IllegalArgumentException iaex) {
throw new ParseException("invalid permission token", 1);
}
}
String topic = tokens[1];
return new Authorization(topic);
return createAuthorization(line, tokens);
case "user":
m_parsingUsersSpecificSection = true;
m_currentUser = tokens[1];
m_parsingPatternSpecificSection = false;
return null;
case "pattern":
//TODO implement the part for patter matching
return null;
m_parsingUsersSpecificSection = false;
m_currentUser = "";
m_parsingPatternSpecificSection = true;
return createAuthorization(line, tokens);
default:
throw new ParseException(String.format("invalid line definition found %s", line), 1);
}
}
@Override
public boolean canWrite(String topic, String user) {
return canDoOperation(topic, WRITE, user);
private Authorization createAuthorization(String line, String[] tokens) throws ParseException {
if (tokens.length > 2) {
//if the tokenized lines has 3 token the second must be the permission
try {
Authorization.Permission permission = Authorization.Permission.valueOf(tokens[1].toUpperCase());
//bring topic with all original spacing
String topic = line.substring(line.indexOf(tokens[2]));
return new Authorization(topic, permission);
} catch (IllegalArgumentException iaex) {
throw new ParseException("invalid permission token", 1);
}
}
String topic = tokens[1];
return new Authorization(topic);
}
@Override
public boolean canRead(String topic, String user) {
return canDoOperation(topic, READ, user);
public boolean canWrite(String topic, String user, String client) {
return canDoOperation(topic, WRITE, user, client);
}
private boolean canDoOperation(String topic, Authorization.Permission permission, String username) {
@Override
public boolean canRead(String topic, String user, String client) {
return canDoOperation(topic, READ, user, client);
}
private boolean canDoOperation(String topic, Authorization.Permission permission, String username, String client) {
for (Authorization auth : m_globalAuthorizations) {
if (auth.permission == permission || auth.permission == READWRITE) {
if (SubscriptionsStore.matchTopics(topic, auth.topic)) {
@@ -120,6 +130,17 @@ class AuthorizationsCollector implements IAuthorizator {
}
}
if (client != null && !client.isEmpty()) {
for (Authorization auth : m_patternAuthorizations) {
String substitutedTopic = auth.topic.replace("%c", client);
if (auth.permission == permission || auth.permission == READWRITE) {
if (SubscriptionsStore.matchTopics(topic, substitutedTopic)) {
return true;
}
}
}
}
if (username != null && !username.isEmpty()) {
if (m_userAuthorizations.containsKey(username)) {
for (Authorization auth : m_userAuthorizations.get(username)) {
@@ -20,12 +20,12 @@ package org.eclipse.moquette.spi.impl.security;
*/
public class DenyAllAuthorizator implements IAuthorizator {
@Override
public boolean canWrite(String topic, String user) {
public boolean canWrite(String topic, String user, String client) {
return true;
}
@Override
public boolean canRead(String topic, String user) {
public boolean canRead(String topic, String user, String client) {
return true;
}
}
@@ -29,7 +29,7 @@ public interface IAuthorizator {
/**
* Ask the implementation of the authorizator if the topic can be used in a publish.
* */
boolean canWrite(String topic, String user);
boolean canWrite(String topic, String user, String client);
boolean canRead(String topic, String user);
boolean canRead(String topic, String user, String client);
}
@@ -20,12 +20,12 @@ package org.eclipse.moquette.spi.impl.security;
*/
public class PermitAllAuthorizator implements IAuthorizator {
@Override
public boolean canWrite(String topic, String user) {
public boolean canWrite(String topic, String user, String client) {
return true;
}
@Override
public boolean canRead(String topic, String user) {
public boolean canRead(String topic, String user, String client) {
return true;
}
}
@@ -39,7 +39,7 @@ public class MapDBPersistentStoreTest {
@Before
public void setUp() throws Exception {
File dbFile = new File(DEFAULT_PERSISTENT_PATH);
assertFalse(String.format("The DB storagefile %s already exists", DEFAULT_PERSISTENT_PATH), dbFile.exists());
assertFalse(String.format("The DB storage file %s already exists", DEFAULT_PERSISTENT_PATH), dbFile.exists());
m_storageService = new MapDBPersistentStore(DEFAULT_PERSISTENT_PATH);
m_storageService.initStore();
@@ -58,8 +58,8 @@ public class ACLFileParserTest {
AuthorizationsCollector authorizations = ACLFileParser.parse(conf);
//Verify
assertTrue(authorizations.canRead("/weather/italy/anemometer", ""));
assertTrue(authorizations.canWrite("/weather/italy/anemometer", ""));
assertTrue(authorizations.canRead("/weather/italy/anemometer", "", ""));
assertTrue(authorizations.canWrite("/weather/italy/anemometer", "", ""));
}
}
@@ -18,6 +18,8 @@ package org.eclipse.moquette.spi.impl.security;
import org.junit.Before;
import org.junit.Test;
import java.io.Reader;
import java.io.StringReader;
import java.text.ParseException;
import static org.junit.Assert.*;
@@ -90,7 +92,7 @@ public class AuthorizationsCollectorTest {
authorizator.parse("topic write /sensors");
//verify
assertTrue(authorizator.canWrite("/sensors", ""));
assertTrue(authorizator.canWrite("/sensors", "", ""));
}
@Test
@@ -98,7 +100,7 @@ public class AuthorizationsCollectorTest {
authorizator.parse("topic read /sensors");
//verify
assertTrue(authorizator.canRead("/sensors", ""));
assertTrue(authorizator.canRead("/sensors", "", ""));
}
@Test
@@ -107,8 +109,8 @@ public class AuthorizationsCollectorTest {
authorizator.parse("topic read /sensors/anemometer");
//verify
assertTrue(authorizator.canWrite("/sensors", ""));
assertFalse(authorizator.canRead("/sensors", ""));
assertTrue(authorizator.canWrite("/sensors", "", ""));
assertFalse(authorizator.canRead("/sensors", "", ""));
}
@Test
@@ -116,7 +118,7 @@ public class AuthorizationsCollectorTest {
authorizator.parse("topic write /sensors/#");
//verify
assertTrue(authorizator.canWrite("/sensors/anemometer/wind", ""));
assertTrue(authorizator.canWrite("/sensors/anemometer/wind", "", ""));
}
@Test
@@ -124,7 +126,7 @@ public class AuthorizationsCollectorTest {
authorizator.parse("topic write /sensors/+");
//verify
assertTrue(authorizator.canWrite("/sensors/anemometer", ""));
assertTrue(authorizator.canWrite("/sensors/anemometer", "", ""));
}
@Test
@@ -133,7 +135,15 @@ public class AuthorizationsCollectorTest {
authorizator.parse("topic write /sensors");
//verify
assertTrue(authorizator.canWrite("/sensors", "john"));
assertFalse(authorizator.canWrite("/sensors", "jack"));
assertTrue(authorizator.canWrite("/sensors", "john", ""));
assertFalse(authorizator.canWrite("/sensors", "jack", ""));
}
@Test
public void testParsePatternClientLineACL() throws ParseException {
authorizator.parse("pattern read /weather/italy/%c");
//Verify
assertTrue(authorizator.canRead("/weather/italy/anemometer1", "", "anemometer1"));
}
}