/* * Licensed 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 com.addthis.hydra.job.auth; import javax.annotation.Nonnull; import javax.annotation.Syntax; import java.io.IOException; import java.util.Objects; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import com.addthis.codec.config.Configs; import com.google.common.collect.ImmutableList; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A wrapper around the static authentication manager * that watches a file for changes and loads the latest * static manager configuration into memory. */ class AuthenticationManagerFileWatch extends AuthenticationManager { private static final Logger log = LoggerFactory.getLogger(AuthenticationManagerFileWatch.class); @Nonnull private volatile AuthenticationManagerStatic manager; @Nonnull private final WatchService watcher; @Nonnull private final Path path; @JsonCreator public AuthenticationManagerFileWatch(@JsonProperty("path") Path path) throws IOException { this.path = path; if (path != null) { @Syntax("HOCON") String content = new String(Files.readAllBytes(path)); this.watcher = FileSystems.getDefault().newWatchService(); this.manager = Configs.decodeObject(AuthenticationManagerStatic.class, content); path.getParent().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY); log.info("Registering file watch authentication"); } else { watcher = null; } } private void updateAuthenticationManager() { WatchKey watchKey = null; try { watchKey = watcher.poll(); if (watchKey == null) { return; } boolean update = false; for (WatchEvent<?> event : watchKey.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); // on overflow go ahead and update the manager if (kind == StandardWatchEventKinds.OVERFLOW) { update = true; break; } Path filepath = ((WatchEvent<Path>) event).context(); if (Objects.equals(path.getFileName(), filepath.getFileName())) { update = true; break; } } if (update) { log.info("Loading most recent version of {}", path); @Syntax("HOCON") String content = new String(Files.readAllBytes(path)); manager = Configs.decodeObject(AuthenticationManagerStatic.class, content); } } catch (IOException ex) { log.warn("IOException during file watch authentication manger: ", ex); } finally { if (watchKey != null) { if(!watchKey.reset()) { log.error("Could not reset watch key for {}", path); } } } } @Override String login(String username, String password, boolean ssl) { updateAuthenticationManager(); return manager.login(username, password, ssl); } @Override public boolean verify(String username, String password, boolean ssl) { updateAuthenticationManager(); return manager.verify(username, password, ssl); } @Override User authenticate(String username, String secret) { updateAuthenticationManager(); return manager.authenticate(username, secret); } @Override User getUser(String username) { updateAuthenticationManager(); return manager.getUser(username); } @Override String sudoToken(String username) { updateAuthenticationManager(); return manager.sudoToken(username); } @Override public void evict(String username) { updateAuthenticationManager(); manager.evict(username); } @Override void logout(String username, String secret) { updateAuthenticationManager(); manager.logout(username, secret); } @Override ImmutableList<String> adminGroups() { updateAuthenticationManager(); return manager.adminGroups(); } @Override ImmutableList<String> adminUsers() { updateAuthenticationManager(); return manager.adminUsers(); } }