/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.apache.karaf.jaas.modules.properties;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.io.Closeable;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.felix.utils.properties.Properties;
import org.apache.karaf.jaas.modules.Encryption;
import org.apache.karaf.jaas.modules.encryption.EncryptionSupport;
import org.apache.karaf.util.StreamUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AutoEncryptionSupport implements Runnable, Closeable {
private final Logger LOGGER = LoggerFactory.getLogger(AutoEncryptionSupport.class);
private volatile boolean running;
private EncryptionSupport encryptionSupport;
private ExecutorService executor;
public AutoEncryptionSupport(Map<String, Object> properties) {
running = true;
encryptionSupport = new EncryptionSupport(properties);
executor = Executors.newSingleThreadExecutor();
executor.execute(this);
}
public void close() {
running = false;
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// Ignore
}
}
@Override
public void run() {
WatchService watchService = null;
try {
watchService = FileSystems.getDefault().newWatchService();
Path dir = Paths.get(System.getProperty("karaf.etc"));
dir.register(watchService, ENTRY_MODIFY);
Path file = dir.resolve("users.properties");
encryptedPassword(new Properties(file.toFile()));
while (running) {
try {
WatchKey key = watchService.poll(1, TimeUnit.SECONDS);
if (key == null) {
continue;
}
for (WatchEvent<?> event : key.pollEvents()) {
@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>)event;
// Context for directory entry event is the file name of entry
Path name = dir.resolve(ev.context());
if (file.equals(name)) {
encryptedPassword(new Properties(file.toFile()));
}
}
key.reset();
} catch (IOException e) {
LOGGER.warn(e.getMessage(), e);
} catch (InterruptedException e) {
// Ignore as this happens on shutdown
}
}
} catch (IOException e) {
LOGGER.warn(e.getMessage(), e);
} finally {
StreamUtils.close(watchService);
}
}
void encryptedPassword(Properties users) throws IOException {
boolean changed = false;
for (String userName : users.keySet()) {
String user = userName;
String userInfos = users.get(user);
if (user.startsWith(PropertiesBackingEngine.GROUP_PREFIX)) {
continue;
}
// the password is in the first position
String[] infos = userInfos.split(",");
String storedPassword = infos[0];
// check if the stored password is flagged as encrypted
String encryptedPassword = getEncryptedPassword(storedPassword);
if (!storedPassword.equals(encryptedPassword)) {
LOGGER.debug("The password isn't flagged as encrypted, encrypt it.");
userInfos = encryptedPassword + ",";
for (int i = 1; i < infos.length; i++) {
if (i == (infos.length - 1)) {
userInfos = userInfos + infos[i];
} else {
userInfos = userInfos + infos[i] + ",";
}
}
if (user.contains("\\")) {
users.remove(user);
user = user.replace("\\", "\\\\");
}
users.put(user, userInfos);
changed = true;
}
}
if (changed) {
users.save();
}
}
String getEncryptedPassword(String password) {
Encryption encryption = encryptionSupport.getEncryption();
String encryptionPrefix = encryptionSupport.getEncryptionPrefix();
String encryptionSuffix = encryptionSupport.getEncryptionSuffix();
if (encryption == null) {
return password;
} else {
boolean prefix = encryptionPrefix == null || password.startsWith(encryptionPrefix);
boolean suffix = encryptionSuffix == null || password.endsWith(encryptionSuffix);
if (prefix && suffix) {
return password;
} else {
String p = encryption.encryptPassword(password);
if (encryptionPrefix != null) {
p = encryptionPrefix + p;
}
if (encryptionSuffix != null) {
p = p + encryptionSuffix;
}
return p;
}
}
}
}