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.security.Principal;
019    import java.sql.Connection;
020    import java.sql.DriverManager;
021    import java.sql.PreparedStatement;
022    import java.sql.ResultSet;
023    import java.sql.SQLException;
024    import java.util.Map;
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.LoginException;
034    
035    import com.hs.mail.security.UserPrincipal;
036    
037    /**
038     * A LoginModule that allows for authentication based on Legacy database.
039     * 
040     * @author Won Chul Doh
041     * @since Jul 18, 2007
042     * 
043     */
044    public class JdbcLoginModule extends BasicLoginModule {
045    
046        private String driver;
047        private String url;
048        private String username;
049        private String password;
050        private String query;
051    
052            @Override
053            public void initialize(Subject subject, CallbackHandler callbackHandler,
054                            Map<String, ?> sharedState, Map<String, ?> options) {
055                    super.initialize(subject, callbackHandler, sharedState, options);
056                    driver = getOption("driver", null);
057                    if (driver == null)
058                            throw new Error("No JDBC driver specified");
059                    try {
060                            Class.forName(driver);
061                    } catch (ClassNotFoundException e) {
062                            throw new RuntimeException("JDBC driver not found: " + driver);
063                    }
064                    url = getOption("url", null);
065                    if (url == null) {
066                            throw new Error("No database URL specified");
067                    }
068                    username = getOption("username", null);
069                    password = getOption("password", null);
070                    if (username == null && password != null || username != null
071                                    && password == null) {
072                            throw new Error(
073                                            "Both username and password option must be specified");
074                    }
075                    query = getOption("query", null);
076                    if (query == null) {
077                            throw new Error("Query not specified");
078                    }
079            }
080            
081            @Override
082            protected Principal[] validate(Callback[] callbacks) throws LoginException {
083                    String username = ((NameCallback) callbacks[0]).getName();
084                    char[] password = ((PasswordCallback) callbacks[1]).getPassword();
085    
086                    Connection cn = null;
087                    ResultSet rs = null;
088                    PreparedStatement pstmt = null;
089                    try {
090                            cn = (this.password != null) 
091                                            ? DriverManager.getConnection(url, this.username, this.password) 
092                                            : DriverManager.getConnection(url);
093                            pstmt = cn.prepareStatement(query);
094                            pstmt.setString(1, username);
095                            rs = pstmt.executeQuery();
096                            if (!rs.next())
097                                    throw new AccountNotFoundException("Account for " + username
098                                                    + " not found");
099                            String encodedPwd = rs.getString(1);
100                            Principal[] principals = new Principal[1];
101                            principals[0] = new UserPrincipal(username);
102                            boolean ok = checkPassword(encodedPwd, password);
103                            if (!ok)
104                                    throw new CredentialException("Incorrect password for "
105                                                    + username);
106                            else
107                                    return principals;
108                    } catch (SQLException e) {
109                            throw new LoginException("Error while reading database: "
110                                            + e.getMessage());
111                    } finally {
112                            try {
113                                    if (pstmt != null)
114                                            pstmt.close();
115                                    if (rs != null)
116                                            rs.close();
117                                    if (cn != null)
118                                            cn.close();
119                            } catch (SQLException e) { }
120                    }
121            }
122    
123    }