001    /*
002     * Copyright 2010 the original author or authors.
003     * 
004     *  Licensed under the Apache License, Version 2.0 (the "License");
005     *  you may not use this file except in compliance with the License.
006     *  You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     *  Unless required by applicable law or agreed to in writing, software
011     *  distributed under the License is distributed on an "AS IS" BASIS,
012     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     *  See the License for the specific language governing permissions and
014     *  limitations under the License.
015     */
016    package com.hs.mail.security.login;
017    
018    import java.io.BufferedInputStream;
019    import java.io.File;
020    import java.io.FileInputStream;
021    import java.io.IOException;
022    import java.security.Principal;
023    import java.util.Map;
024    import java.util.StringTokenizer;
025    
026    import javax.security.auth.Subject;
027    import javax.security.auth.callback.Callback;
028    import javax.security.auth.callback.CallbackHandler;
029    import javax.security.auth.callback.NameCallback;
030    import javax.security.auth.callback.PasswordCallback;
031    import javax.security.auth.login.AccountNotFoundException;
032    import javax.security.auth.login.CredentialException;
033    import javax.security.auth.login.FailedLoginException;
034    import javax.security.auth.login.LoginException;
035    
036    import org.apache.commons.io.IOUtils;
037    
038    import com.hs.mail.io.LineReader;
039    import com.hs.mail.security.RolePrincipal;
040    import com.hs.mail.security.UserPrincipal;
041    
042    /**
043     * A LoginModule that allows for authentication based on properties file.
044     * 
045     * @author Won Chul Doh
046     * @since Aug 5, 2010
047     *
048     */
049    public class PropertiesLoginModule extends BasicLoginModule {
050    
051            private File file;
052            
053            @Override
054            public void initialize(Subject subject, CallbackHandler callbackHandler,
055                            Map<String, ?> sharedState, Map<String, ?> options) {
056                    super.initialize(subject, callbackHandler, sharedState, options);
057                    
058                    File baseDir = null;
059                    if (System.getProperty("java.security.auth.login.config") != null) {
060                            baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
061                    } else {
062                            baseDir = new File(".");
063                    }
064    
065                    String filename = getOption("file", null);
066                    if (filename == null)
067                            throw new RuntimeException("No file specified");
068                    file = new File(baseDir, filename);
069            }
070    
071            @Override
072            protected Principal[] validate(Callback[] callbacks) throws LoginException {
073                    String username = ((NameCallback) callbacks[0]).getName();
074                    char[] password = ((PasswordCallback) callbacks[1]).getPassword();
075    
076                    String entry = getLine(file, username + "=");
077                    if (entry == null)
078                            throw new AccountNotFoundException("Account for " + username
079                                            + " not found");
080                    int index = entry.indexOf('=');
081                    if (index == -1)
082                            throw new FailedLoginException("Invalid user record");
083                    entry = entry.substring(index + 1);
084                    index = entry.indexOf(':');
085                    if (index == -1)
086                            throw new FailedLoginException("Invalid user record");
087                    String encodedPwd = entry.substring(0, index);
088                    String roles = entry.substring(index + 1);
089                    StringTokenizer st = new StringTokenizer(roles, ",");
090                    Principal[] principals = new Principal[st.countTokens() + 1];
091                    for (int i = 0; i < principals.length -1; i++) {
092                            principals[i] = new RolePrincipal(st.nextToken().trim());
093                    }
094                    principals[principals.length - 1] = new UserPrincipal(username);
095                    boolean ok = checkPassword(encodedPwd, password);
096                    if (!ok)
097                            throw new CredentialException("Incorrect password for " + username);
098                    else
099                            return principals;
100            }
101    
102            private String getLine(File file, String start) throws LoginException {
103                    LineReader reader = null;
104                    BufferedInputStream is = null;
105                    try {
106                            is = new BufferedInputStream(new FileInputStream(file));
107                            reader = new LineReader(is);
108                            int len = start.length();
109                            String line = null;
110                            while ((line = reader.readLine()) != null) {
111                                    line = line.trim();
112                                    if (!line.startsWith("#")
113                                                    && line.regionMatches(false, 0, start, 0, len)) {
114                                            return line;
115                                    }
116                            }
117                            return null;
118                    } catch (IOException e) {
119                            throw new LoginException("Error while reading file: " + file);
120                    } finally {
121                            IOUtils.closeQuietly(is);
122                            IOUtils.closeQuietly(reader);
123                    }
124            }
125            
126    }