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 }