Moved packages from old io.moquette.server to new io.moquette.broker
This commit is contained in:
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.text.ParseException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Parses the acl configuration file. If a line starts with # it's comment. Blank lines are skipped.
|
||||
* The format is "topic [read|write|readwrite] {topic name}"
|
||||
*/
|
||||
public final class ACLFileParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ACLFileParser.class);
|
||||
|
||||
/**
|
||||
* Parse the configuration from file.
|
||||
*
|
||||
* @param file
|
||||
* to parse
|
||||
* @return the collector of authorizations form reader passed into.
|
||||
* @throws ParseException
|
||||
* if the format is not compliant.
|
||||
*/
|
||||
public static AuthorizationsCollector parse(File file) throws ParseException {
|
||||
if (file == null) {
|
||||
LOG.warn("parsing NULL file, so fallback on default configuration!");
|
||||
return AuthorizationsCollector.emptyImmutableCollector();
|
||||
}
|
||||
if (!file.exists()) {
|
||||
LOG.warn(
|
||||
String.format(
|
||||
"parsing not existing file %s, so fallback on default configuration!",
|
||||
file.getAbsolutePath()));
|
||||
return AuthorizationsCollector.emptyImmutableCollector();
|
||||
}
|
||||
try {
|
||||
Reader reader = Files.newBufferedReader(file.toPath(), UTF_8);
|
||||
return parse(reader);
|
||||
} catch (IOException fex) {
|
||||
LOG.warn(
|
||||
String.format(
|
||||
"parsing not existing file %s, so fallback on default configuration!",
|
||||
file.getAbsolutePath()),
|
||||
fex);
|
||||
return AuthorizationsCollector.emptyImmutableCollector();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the ACL configuration file
|
||||
*
|
||||
* @param reader
|
||||
* to parse
|
||||
* @return the collector of authorizations form reader passed into.
|
||||
* @throws ParseException
|
||||
* if the format is not compliant.
|
||||
*/
|
||||
public static AuthorizationsCollector parse(Reader reader) throws ParseException {
|
||||
if (reader == null) {
|
||||
// just log and return default properties
|
||||
LOG.warn("parsing NULL reader, so fallback on default configuration!");
|
||||
return AuthorizationsCollector.emptyImmutableCollector();
|
||||
}
|
||||
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
String line;
|
||||
AuthorizationsCollector collector = new AuthorizationsCollector();
|
||||
|
||||
Pattern emptyLine = Pattern.compile("^\\s*$");
|
||||
Pattern commentLine = Pattern.compile("^#.*"); // As spec, comment lines should start with '#'
|
||||
Pattern invalidCommentLine = Pattern.compile("^\\s*#.*");
|
||||
// This pattern has a dependency on filtering `commentLine`.
|
||||
Pattern endLineComment = Pattern.compile("^([\\w\\s\\/\\+]+#?)(\\s*#.*)$");
|
||||
Matcher endLineCommentMatcher;
|
||||
|
||||
try {
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.isEmpty() || emptyLine.matcher(line).matches() || commentLine.matcher(line).matches()) {
|
||||
// skip it's a black line or comment
|
||||
continue;
|
||||
} else if (invalidCommentLine.matcher(line).matches()) {
|
||||
// it's a malformed comment
|
||||
int commentMarker = line.indexOf('#');
|
||||
throw new ParseException(line, commentMarker);
|
||||
}
|
||||
|
||||
endLineCommentMatcher = endLineComment.matcher(line);
|
||||
if (endLineCommentMatcher.matches()) {
|
||||
line = endLineCommentMatcher.group(1);
|
||||
}
|
||||
|
||||
collector.parse(line);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ParseException("Failed to read", 1);
|
||||
}
|
||||
return collector;
|
||||
}
|
||||
|
||||
private ACLFileParser() {
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import io.moquette.spi.security.IAuthenticator;
|
||||
|
||||
public class AcceptAllAuthenticator implements IAuthenticator {
|
||||
|
||||
@Override
|
||||
public boolean checkValid(String clientId, String username, byte[] password) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import static io.moquette.spi.impl.security.Authorization.Permission.READWRITE;
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
|
||||
/**
|
||||
* Carries the read/write authorization to topics for the users.
|
||||
*/
|
||||
public class Authorization {
|
||||
|
||||
protected final Topic topic;
|
||||
protected final Permission permission;
|
||||
|
||||
/**
|
||||
* Access rights
|
||||
*/
|
||||
enum Permission {
|
||||
READ, WRITE, READWRITE
|
||||
}
|
||||
|
||||
Authorization(Topic topic) {
|
||||
this(topic, Permission.READWRITE);
|
||||
}
|
||||
|
||||
Authorization(Topic topic, Permission permission) {
|
||||
this.topic = topic;
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
public boolean grant(Permission desiredPermission) {
|
||||
return permission == desiredPermission || permission == READWRITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
Authorization that = (Authorization) o;
|
||||
|
||||
if (permission != that.permission)
|
||||
return false;
|
||||
if (!topic.equals(that.topic))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = topic.hashCode();
|
||||
result = 31 * result + permission.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.security.IAuthorizatorPolicy;
|
||||
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<Authorization> m_globalAuthorizations = new ArrayList<>();
|
||||
private List<Authorization> m_patternAuthorizations = new ArrayList<>();
|
||||
private Map<String, List<Authorization>> 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<Authorization>());
|
||||
}
|
||||
List<Authorization> 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<Authorization> auths = m_userAuthorizations.get(username);
|
||||
if (matchACL(auths, topic, permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean matchACL(List<Authorization> 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();
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.security.IAuthenticator;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Load user credentials from a SQL database. sql driver must be provided at runtime
|
||||
*/
|
||||
public class DBAuthenticator implements IAuthenticator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DBAuthenticator.class);
|
||||
|
||||
private final MessageDigest messageDigest;
|
||||
private HikariDataSource dataSource;
|
||||
private String sqlQuery;
|
||||
|
||||
public DBAuthenticator(IConfig conf) {
|
||||
this(conf.getProperty(BrokerConstants.DB_AUTHENTICATOR_DRIVER, ""),
|
||||
conf.getProperty(BrokerConstants.DB_AUTHENTICATOR_URL, ""),
|
||||
conf.getProperty(BrokerConstants.DB_AUTHENTICATOR_QUERY, ""),
|
||||
conf.getProperty(BrokerConstants.DB_AUTHENTICATOR_DIGEST, ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* provide authenticator from SQL database
|
||||
*
|
||||
* @param driver
|
||||
* : jdbc driver class like : "org.postgresql.Driver"
|
||||
* @param jdbcUrl
|
||||
* : jdbc url like : "jdbc:postgresql://host:port/dbname"
|
||||
* @param sqlQuery
|
||||
* : sql query like : "SELECT PASSWORD FROM USER WHERE LOGIN=?"
|
||||
* @param digestMethod
|
||||
* : password encoding algorithm : "MD5", "SHA-1", "SHA-256"
|
||||
*/
|
||||
public DBAuthenticator(String driver, String jdbcUrl, String sqlQuery, String digestMethod) {
|
||||
this.sqlQuery = sqlQuery;
|
||||
this.dataSource = new HikariDataSource();
|
||||
this.dataSource.setJdbcUrl(jdbcUrl);
|
||||
|
||||
try {
|
||||
this.messageDigest = MessageDigest.getInstance(digestMethod);
|
||||
} catch (NoSuchAlgorithmException nsaex) {
|
||||
LOG.error(String.format("Can't find %s for password encoding", digestMethod), nsaex);
|
||||
throw new RuntimeException(nsaex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean checkValid(String clientId, String username, byte[] password) {
|
||||
// Check Username / Password in DB using sqlQuery
|
||||
if (username == null || password == null) {
|
||||
LOG.info("username or password was null");
|
||||
return false;
|
||||
}
|
||||
|
||||
ResultSet resultSet = null;
|
||||
PreparedStatement preparedStatement = null;
|
||||
Connection conn = null;
|
||||
try {
|
||||
conn = this.dataSource.getConnection();
|
||||
|
||||
preparedStatement = conn.prepareStatement(this.sqlQuery);
|
||||
preparedStatement.setString(1, username);
|
||||
resultSet = preparedStatement.executeQuery();
|
||||
if (resultSet.next()) {
|
||||
final String foundPwq = resultSet.getString(1);
|
||||
messageDigest.update(password);
|
||||
byte[] digest = messageDigest.digest();
|
||||
String encodedPasswd = new String(Hex.encodeHex(digest));
|
||||
return foundPwq.equals(encodedPasswd);
|
||||
}
|
||||
} catch (SQLException sqlex) {
|
||||
LOG.error("Error quering DB for username: {}", username, sqlex);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
if (preparedStatement != null) {
|
||||
preparedStatement.close();
|
||||
}
|
||||
if (conn != null) {
|
||||
conn.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LOG.error("Error releasing connection to the datasource", username, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.security.IAuthorizatorPolicy;
|
||||
|
||||
public class DenyAllAuthorizatorPolicy implements IAuthorizatorPolicy {
|
||||
|
||||
@Override
|
||||
public boolean canWrite(Topic topic, String user, String client) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead(Topic topic, String user, String client) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import io.moquette.server.config.FileResourceLoader;
|
||||
|
||||
/**
|
||||
* Load user credentials from a text file. Each line of the file is formatted as
|
||||
* "[username]:[sha256(password)]". The username mustn't contains : char.
|
||||
*
|
||||
* To encode your password from command line on Linux systems, you could use:
|
||||
*
|
||||
* <pre>
|
||||
* echo -n "yourpassword" | sha256sum
|
||||
* </pre>
|
||||
*
|
||||
* NB -n is important because echo append a newline by default at the of string. -n avoid this
|
||||
* behaviour.
|
||||
*
|
||||
* @deprecated user {@link ResourceAuthenticator} instead
|
||||
*/
|
||||
public class FileAuthenticator extends ResourceAuthenticator {
|
||||
|
||||
public FileAuthenticator(String parent, String filePath) {
|
||||
super(new FileResourceLoader(parent), filePath);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import io.moquette.spi.impl.subscriptions.Topic;
|
||||
import io.moquette.spi.security.IAuthorizatorPolicy;
|
||||
|
||||
public class PermitAllAuthorizatorPolicy implements IAuthorizatorPolicy {
|
||||
|
||||
@Override
|
||||
public boolean canWrite(Topic topic, String user, String client) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead(Topic topic, String user, String client) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* 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 io.moquette.spi.impl.security;
|
||||
|
||||
import io.moquette.server.config.IResourceLoader;
|
||||
import io.moquette.spi.security.IAuthenticator;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Load user credentials from a text resource. Each line of the file is formatted as
|
||||
* "[username]:[sha256(password)]". The username mustn't contains : char.
|
||||
*
|
||||
* To encode your password from command line on Linux systems, you could use:
|
||||
*
|
||||
* <pre>
|
||||
* echo -n "yourpassword" | sha256sum
|
||||
* </pre>
|
||||
*
|
||||
* NB -n is important because echo append a newline by default at the of string. -n avoid this
|
||||
* behaviour.
|
||||
*/
|
||||
public class ResourceAuthenticator implements IAuthenticator {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(ResourceAuthenticator.class);
|
||||
|
||||
private Map<String, String> m_identities = new HashMap<>();
|
||||
|
||||
public ResourceAuthenticator(IResourceLoader resourceLoader, String resourceName) {
|
||||
try {
|
||||
MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException nsaex) {
|
||||
LOG.error("Can't find SHA-256 for password encoding", nsaex);
|
||||
throw new RuntimeException(nsaex);
|
||||
}
|
||||
|
||||
LOG.info(String.format("Loading password %s %s", resourceLoader.getName(), resourceName));
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = resourceLoader.loadResource(resourceName);
|
||||
if (reader == null) {
|
||||
LOG.warn(String.format("Parsing not existing %s %s", resourceLoader.getName(), resourceName));
|
||||
} else {
|
||||
parse(reader);
|
||||
}
|
||||
} catch (IResourceLoader.ResourceIsDirectoryException e) {
|
||||
LOG.warn(String.format("Trying to parse directory %s", resourceName));
|
||||
} catch (ParseException pex) {
|
||||
LOG.warn(
|
||||
String.format("Format error in parsing password %s %s", resourceLoader.getName(), resourceName),
|
||||
pex);
|
||||
}
|
||||
}
|
||||
|
||||
private void parse(Reader reader) throws ParseException {
|
||||
if (reader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedReader br = new BufferedReader(reader);
|
||||
String line;
|
||||
try {
|
||||
while ((line = br.readLine()) != null) {
|
||||
int commentMarker = line.indexOf('#');
|
||||
if (commentMarker != -1) {
|
||||
if (commentMarker == 0) {
|
||||
// skip its a comment
|
||||
continue;
|
||||
} else {
|
||||
// it's a malformed comment
|
||||
throw new ParseException(line, commentMarker);
|
||||
}
|
||||
} else {
|
||||
if (line.isEmpty() || line.matches("^\\s*$")) {
|
||||
// skip it's a black line
|
||||
continue;
|
||||
}
|
||||
|
||||
// split till the first space
|
||||
int delimiterIdx = line.indexOf(':');
|
||||
String username = line.substring(0, delimiterIdx).trim();
|
||||
String password = line.substring(delimiterIdx + 1).trim();
|
||||
|
||||
m_identities.put(username, password);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new ParseException("Failed to read", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkValid(String clientId, String username, byte[] password) {
|
||||
if (username == null || password == null) {
|
||||
LOG.info("username or password was null");
|
||||
return false;
|
||||
}
|
||||
String foundPwq = m_identities.get(username);
|
||||
if (foundPwq == null) {
|
||||
return false;
|
||||
}
|
||||
String encodedPasswd = DigestUtils.sha256Hex(password);
|
||||
return foundPwq.equals(encodedPasswd);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user