com.github.netty.protocol.mqtt.security.AuthorizationsCollector Maven / Gradle / Ivy
/*
* Copyright (c) 2012-2018 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package com.github.netty.protocol.mqtt.security;
import com.github.netty.protocol.mqtt.subscriptions.Topic;
import java.text.ParseException;
import java.util.*;
/**
* Used by the ACLFileParser to push all authorizations it finds. ACLAuthorizator uses it in read
* mode to check it topics matches the ACLs.
*
* Not thread safe.
*/
class AuthorizationsCollector implements IAuthorizatorPolicy {
private List m_globalAuthorizations = new ArrayList<>();
private List m_patternAuthorizations = new ArrayList<>();
private Map> m_userAuthorizations = new HashMap<>();
private boolean m_parsingUsersSpecificSection;
private boolean m_parsingPatternSpecificSection;
private String m_currentUser = "";
static final AuthorizationsCollector emptyImmutableCollector() {
AuthorizationsCollector coll = new AuthorizationsCollector();
coll.m_globalAuthorizations = Collections.emptyList();
coll.m_patternAuthorizations = Collections.emptyList();
coll.m_userAuthorizations = Collections.emptyMap();
return coll;
}
void parse(String line) throws ParseException {
Authorization acl = parseAuthLine(line);
if (acl == null) {
// skip it's a user
return;
}
if (m_parsingUsersSpecificSection) {
// TODO in java 8 switch to m_userAuthorizations.putIfAbsent(m_currentUser, new
// ArrayList());
if (!m_userAuthorizations.containsKey(m_currentUser)) {
m_userAuthorizations.put(m_currentUser, new ArrayList());
}
List userAuths = m_userAuthorizations.get(m_currentUser);
userAuths.add(acl);
} else if (m_parsingPatternSpecificSection) {
m_patternAuthorizations.add(acl);
} else {
m_globalAuthorizations.add(acl);
}
}
protected Authorization parseAuthLine(String line) throws ParseException {
String[] tokens = line.split("\\s+");
String keyword = tokens[0].toLowerCase();
switch (keyword) {
case "topic":
return createAuthorization(line, tokens);
case "user":
m_parsingUsersSpecificSection = true;
m_currentUser = tokens[1];
m_parsingPatternSpecificSection = false;
return null;
case "pattern":
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);
}
}
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
Topic topic = new Topic(line.substring(line.indexOf(tokens[2])));
return new Authorization(topic, permission);
} catch (IllegalArgumentException iaex) {
throw new ParseException("invalid permission token", 1);
}
}
Topic topic = new Topic(tokens[1]);
return new Authorization(topic);
}
@Override
public boolean canWrite(Topic topic, String user, String client) {
return canDoOperation(topic, Authorization.Permission.WRITE, user, client);
}
@Override
public boolean canRead(Topic topic, String user, String client) {
return canDoOperation(topic, Authorization.Permission.READ, user, client);
}
private boolean canDoOperation(Topic topic, Authorization.Permission permission, String username, String client) {
if (matchACL(m_globalAuthorizations, topic, permission)) {
return true;
}
if (isNotEmpty(client) || isNotEmpty(username)) {
for (Authorization auth : m_patternAuthorizations) {
Topic substitutedTopic = new Topic(auth.topic.toString().replace("%c", client).replace("%u", username));
if (auth.grant(permission)) {
if (topic.match(substitutedTopic)) {
return true;
}
}
}
}
if (isNotEmpty(username)) {
if (m_userAuthorizations.containsKey(username)) {
List auths = m_userAuthorizations.get(username);
return matchACL(auths, topic, permission);
}
}
return false;
}
private boolean matchACL(List auths, Topic topic, Authorization.Permission permission) {
for (Authorization auth : auths) {
if (auth.grant(permission)) {
if (topic.match(auth.topic)) {
return true;
}
}
}
return false;
}
private boolean isNotEmpty(String client) {
return client != null && !client.isEmpty();
}
public boolean isEmpty() {
return m_globalAuthorizations.isEmpty();
}
}