001 /**
002 *
003 * Copyright 2003-2004 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.security.realm.providers;
018
019 import java.io.File;
020 import java.io.FileOutputStream;
021 import java.io.IOException;
022 import java.io.PrintWriter;
023 import java.nio.channels.FileChannel;
024 import java.nio.channels.FileLock;
025 import java.text.DateFormat;
026 import java.text.SimpleDateFormat;
027 import java.util.Date;
028 import java.util.Map;
029 import javax.security.auth.Subject;
030 import javax.security.auth.callback.Callback;
031 import javax.security.auth.callback.CallbackHandler;
032 import javax.security.auth.callback.NameCallback;
033 import javax.security.auth.login.LoginException;
034 import javax.security.auth.spi.LoginModule;
035
036 import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
037 import org.apache.geronimo.system.serverinfo.ServerInfo;
038
039 /**
040 * Writes audit records to a file for all authentication activity. Currently
041 * doesn't perform too well; perhaps the file management should be centralized
042 * and the IO objects kept open across many requests. It would also be nice
043 * to write in a more convenient XML format.
044 *
045 * This module does not write any Principals into the Subject.
046 *
047 * To enable this login module, set your primary login module to REQUIRED or
048 * OPTIONAL, and list this module after it (with any setting).
049 *
050 * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $
051 */
052 public class FileAuditLoginModule implements LoginModule {
053 public static final String LOG_FILE_OPTION = "file";
054 private final static DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
055 private File logFile;
056 private CallbackHandler handler;
057 private String username;
058
059 public void initialize(Subject subject, CallbackHandler callbackHandler,
060 Map sharedState, Map options) {
061 String name = (String) options.get(LOG_FILE_OPTION);
062 ServerInfo info = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION);
063 logFile = info.resolve(name);
064 handler = callbackHandler;
065 }
066
067 public boolean login() throws LoginException {
068 NameCallback user = new NameCallback("User name:");
069 Callback[] callbacks = new Callback[]{user};
070 try {
071 handler.handle(callbacks);
072 } catch (Exception e) {
073 throw new LoginException("Unable to process callback: "+e);
074 }
075 if(callbacks.length != 1) {
076 throw new IllegalStateException("Number of callbacks changed by server!");
077 }
078 user = (NameCallback) callbacks[0];
079 username = user.getName();
080 writeToFile("Authentication attempt");
081
082 return true;
083 }
084
085 private synchronized void writeToFile(String action) {
086 Date date = new Date();
087 try {
088 FileOutputStream out = new FileOutputStream(logFile, true);
089 FileChannel channel = out.getChannel();
090 FileLock lock = channel.lock(0, Long.MAX_VALUE, false);
091 PrintWriter writer = new PrintWriter(out, false);
092 writer.println(DATE_FORMAT.format(date)+" - "+action+" - "+username);
093 writer.flush();
094 writer.close();
095 if(lock.isValid()) {
096 lock.release();
097 }
098 } catch (IOException e) {
099 throw new RuntimeException("Unable to write to authentication log file", e);
100 }
101 }
102
103 public boolean commit() throws LoginException {
104 writeToFile("Authentication succeeded");
105 return true;
106 }
107
108 public boolean abort() throws LoginException {
109 if(username != null) { //work around initial "fake" login
110 writeToFile("Authentication failed");
111 username = null;
112 }
113 return true;
114 }
115
116 public boolean logout() throws LoginException {
117 writeToFile("Explicit logout");
118 username = null;
119 return true;
120 }
121 }