001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 * This login module does not check credentials so it should never be able to cause a login to succeed. 051 * Therefore the lifecycle methods must return false to indicate success or throw a LoginException to indicate failure. 052 * 053 * @version $Rev: 565912 $ $Date: 2007-08-14 17:03:11 -0400 (Tue, 14 Aug 2007) $ 054 */ 055 public class FileAuditLoginModule implements LoginModule { 056 public static final String LOG_FILE_OPTION = "file"; 057 private final static DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); 058 private File logFile; 059 private CallbackHandler handler; 060 private String username; 061 062 public void initialize(Subject subject, CallbackHandler callbackHandler, 063 Map sharedState, Map options) { 064 String name = (String) options.get(LOG_FILE_OPTION); 065 ServerInfo info = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION); 066 logFile = info.resolve(name); 067 handler = callbackHandler; 068 } 069 070 public boolean login() throws LoginException { 071 NameCallback user = new NameCallback("User name:"); 072 Callback[] callbacks = new Callback[]{user}; 073 try { 074 handler.handle(callbacks); 075 } catch (Exception e) { 076 throw (LoginException)new LoginException("Unable to process callback: "+e.getMessage()).initCause(e); 077 } 078 if(callbacks.length != 1) { 079 throw new IllegalStateException("Number of callbacks changed by server!"); 080 } 081 user = (NameCallback) callbacks[0]; 082 username = user.getName(); 083 writeToFile("Authentication attempt"); 084 085 return false; 086 } 087 088 private synchronized void writeToFile(String action) { 089 Date date = new Date(); 090 try { 091 FileOutputStream out = new FileOutputStream(logFile, true); 092 FileChannel channel = out.getChannel(); 093 FileLock lock = channel.lock(0, Long.MAX_VALUE, false); 094 PrintWriter writer = new PrintWriter(out, false); 095 writer.println(DATE_FORMAT.format(date)+" - "+action+" - "+username); 096 writer.flush(); 097 writer.close(); 098 if(lock.isValid()) { 099 lock.release(); 100 } 101 } catch (IOException e) { 102 throw new RuntimeException("Unable to write to authentication log file", e); 103 } 104 } 105 106 public boolean commit() throws LoginException { 107 writeToFile("Authentication succeeded"); 108 return false; 109 } 110 111 public boolean abort() throws LoginException { 112 if(username != null) { //work around initial "fake" login 113 writeToFile("Authentication failed"); 114 username = null; 115 } 116 return false; 117 } 118 119 public boolean logout() throws LoginException { 120 writeToFile("Explicit logout"); 121 username = null; 122 return false; 123 } 124 }