java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java
changeset 7 46bb283a674d
parent 6 aff44e80f418
     1.1 --- a/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java	Tue Feb 07 00:27:39 2012 +0100
     1.2 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java	Tue Feb 07 18:03:31 2012 +0100
     1.3 @@ -1,8 +1,6 @@
     1.4  package cz.frantovo.jaas.sql;
     1.5  
     1.6 -import com.sun.appserv.security.AppservRealm;
     1.7  import com.sun.enterprise.security.auth.realm.BadRealmException;
     1.8 -import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
     1.9  import com.sun.enterprise.security.auth.realm.NoSuchUserException;
    1.10  import java.sql.Connection;
    1.11  import java.sql.PreparedStatement;
    1.12 @@ -13,7 +11,6 @@
    1.13  import java.util.Collection;
    1.14  import java.util.Collections;
    1.15  import java.util.Enumeration;
    1.16 -import java.util.Properties;
    1.17  import java.util.logging.Level;
    1.18  import javax.naming.InitialContext;
    1.19  import javax.naming.NamingException;
    1.20 @@ -25,52 +22,113 @@
    1.21   *
    1.22   * @author fiki
    1.23   */
    1.24 -public class SQLRealm extends AppservRealm {
    1.25 +public class SQLRealm extends ParametrizovanýRealm {
    1.26  
    1.27  	private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi.";
    1.28 -	private static final String PARAM_JNDI = "jndi";
    1.29 -	private static final String PARAM_SQL_HESLO = "sql_heslo";
    1.30 -	private static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele";
    1.31 -	private static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny";
    1.32 -	private DataSource datovýZdroj;
    1.33 +	/**
    1.34 +	 * <p>JNDI jméno datového zdroje – odkazuje na instanci {@linkplain javax.sql.DataSource}</p>
    1.35 +	 *
    1.36 +	 * <p>Příklady:</p>
    1.37 +	 *
    1.38 +	 * <ul>
    1.39 +	 * <li><code>jdbc/mojeAplikace</code></li>
    1.40 +	 * </ul>
    1.41 +	 */
    1.42 +	public static final String PARAM_JNDI = "jndi";
    1.43 +	/** Pokud je nastaveno na "true", nebude se při inicializaci testovat spojení s databází */
    1.44 +	public static final String PARAM_NETESTOVAT_SPOJENÍ = "netestovat_spojeni";
    1.45 +	/**
    1.46 +	 * <p>SQL dotaz pro kontrolu hesla.</p>
    1.47 +	 *
    1.48 +	 * <p>Má dva parametry:</p>
    1.49 +	 * <ol>
    1.50 +	 * <li>uživatelské jméno</li>
    1.51 +	 * <li>heslo</li>
    1.52 +	 * </ol>
    1.53 +	 *
    1.54 +	 * <p>Vrací:</p>
    1.55 +	 * <ul>
    1.56 +	 * <li>Při úspěšném ověření jednu hodnotu: uživatelské jméno</li>
    1.57 +	 * <li>Při neexistujícím uživateli nebo špatném hesle: prázdný výsledek (nula řádků)</li>
    1.58 +	 * <li>Při jiné chybě: vyhazuje výjimku</li>
    1.59 +	 * </ul>
    1.60 +	 *
    1.61 +	 * <p>Příklady:</p>
    1.62 +	 *
    1.63 +	 * <ul>
    1.64 +	 * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?</code></li>
    1.65 +	 * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = sha1(?)</code></li>
    1.66 +	 * <li><code>SELECT zkontroluj_heslo(?, ?)</code></li>
    1.67 +	 * </ul>
    1.68 +	 *
    1.69 +	 * <p>(hashovací a jiné funkce musí být samozřejmě podporované databází)</p>
    1.70 +	 */
    1.71 +	public static final String PARAM_SQL_HESLO = "sql_heslo";
    1.72 +	/**
    1.73 +	 * <p>SQL dotaz pro zjištění skupin daného uživatele.</p>
    1.74 +	 *
    1.75 +	 * <p>Má jeden parametr: jméno uživatele</p>
    1.76 +	 *
    1.77 +	 * <p>Příklady:</p>
    1.78 +	 *
    1.79 +	 * <ul>
    1.80 +	 * <li><code>SELECT nazev FROM skupina WHERE uzivatel = ?</code></li>
    1.81 +	 * </ul>
    1.82 +	 */
    1.83 +	public static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele";
    1.84 +	/**
    1.85 +	 * <p>SQL dotaz pro zjištění všech skupin v dané bezpečnostní doméně.</p>
    1.86 +	 *
    1.87 +	 * <p>Příklady:</p>
    1.88 +	 *
    1.89 +	 * <ul>
    1.90 +	 * <li><code>SELECT nazev FROM skupina</code></li>
    1.91 +	 * </ul>
    1.92 +	 */
    1.93 +	public static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny";
    1.94 +	private String jndiDatovéhoZdroje;
    1.95  	private String sqlHeslo;
    1.96  	private String sqlSkupinyUživatele;
    1.97  	private String sqlSkupinyVšechny;
    1.98  
    1.99 -	/**
   1.100 -	 * Načteme a zkontrolujeme parametry
   1.101 -	 *
   1.102 -	 * @param parametry
   1.103 -	 * @throws BadRealmException pokud je v parametrech chyba
   1.104 -	 * @throws NoSuchRealmException
   1.105 -	 */
   1.106  	@Override
   1.107 -	public void init(Properties parametry) throws BadRealmException, NoSuchRealmException {
   1.108 -		super.init(parametry);
   1.109 -
   1.110 -		String jaasContext = parametry.getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT);
   1.111 -		setProperty(JAAS_CONTEXT_PARAM, jaasContext);
   1.112 +	protected void parametrizuj() throws BadRealmException {
   1.113 +		String jaasContext = getProperties().getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT);
   1.114 +		getProperties().setProperty(JAAS_CONTEXT_PARAM, jaasContext);
   1.115  
   1.116  		/** Databázové spojení */
   1.117 -		{
   1.118 -			String jndi = najdiParametr(parametry, PARAM_JNDI, "název datového zdroje");
   1.119 +		jndiDatovéhoZdroje = najdiParametr(PARAM_JNDI, "název datového zdroje");
   1.120 +		testSpojení();
   1.121 +
   1.122 +		/** SQL dotazy */
   1.123 +		sqlHeslo = najdiParametr(PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla");
   1.124 +		sqlSkupinyUživatele = najdiParametr(PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele");
   1.125 +		sqlSkupinyVšechny = najdiParametr(PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin");
   1.126 +
   1.127 +		_logger.log(Level.INFO, "SQLRealm úspěšně parametrizován. JaasContext: {0}, počet parametrů: {1}", new Object[]{getJAASContext(), getProperties().size()});
   1.128 +	}
   1.129 +
   1.130 +	private void testSpojení() throws BadRealmException {
   1.131 +		if (Boolean.valueOf(getProperty(PARAM_NETESTOVAT_SPOJENÍ))) {
   1.132 +			_logger.log(Level.WARNING, "Netestujeme databázové spojení při inicializaci.");
   1.133 +		} else {
   1.134  			try {
   1.135 -				InitialContext k = new InitialContext();
   1.136 -				jndi = parametry.getProperty(PARAM_JNDI);
   1.137 -				datovýZdroj = (DataSource) k.lookup(jndi);
   1.138 +				Connection s = getSpojení();
   1.139 +				s.close();
   1.140  			} catch (NamingException e) {
   1.141 -				throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndi, e);
   1.142 +				throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndiDatovéhoZdroje, e);
   1.143 +			} catch (SQLException e) {
   1.144 +				throw new BadRealmException("Nepodařilo se navázat spojení s DB datového zdroje: " + jndiDatovéhoZdroje, e);
   1.145  			}
   1.146  		}
   1.147 +	}
   1.148  
   1.149 -		/** SQL dotazy */
   1.150 -		sqlHeslo = najdiParametr(parametry, PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla");
   1.151 -		sqlSkupinyUživatele = najdiParametr(parametry, PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele");
   1.152 -		sqlSkupinyVšechny = najdiParametr(parametry, PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin");
   1.153 +	private Connection getSpojení() throws NamingException, SQLException {
   1.154 +		InitialContext k = new InitialContext();
   1.155 +		DataSource datovýZdroj = (DataSource) k.lookup(jndiDatovéhoZdroje);
   1.156 +		return datovýZdroj.getConnection();
   1.157 +	}
   1.158  
   1.159 -		_logger.log(Level.INFO, "SQLRealm úspěšně vytvořen. JaasContext: {0}", jaasContext);
   1.160 -	}
   1.161 -	
   1.162  	@Override
   1.163  	public String getAuthType() {
   1.164  		return AUTH_TYPE;
   1.165 @@ -83,15 +141,41 @@
   1.166  	 */
   1.167  	@Override
   1.168  	public Enumeration<String> getGroupNames(String uživatel) throws NoSuchUserException {
   1.169 +		try {
   1.170 +			return getSkupiny(sqlSkupinyUživatele, uživatel);
   1.171 +		} catch (NamingException | SQLException e) {
   1.172 +			String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + ".";
   1.173 +			_logger.log(Level.WARNING, hláška, e);
   1.174 +			throw new NoSuchUserException(hláška);
   1.175 +		}
   1.176 +	}
   1.177 +
   1.178 +	/**
   1.179 +	 * @return seznam všech skupin v této bezpečnostní doméně
   1.180 +	 * @throws BadRealmException v případě SQL chyby
   1.181 +	 */
   1.182 +	@Override
   1.183 +	public Enumeration getGroupNames() throws BadRealmException {
   1.184 +		try {
   1.185 +			return getSkupiny(sqlSkupinyVšechny, null);
   1.186 +		} catch (NamingException | SQLException e) {
   1.187 +			throw new BadRealmException("Chyba při zjišťování seznamu všech skupin.", e);
   1.188 +		}
   1.189 +	}
   1.190 +
   1.191 +	public Enumeration<String> getSkupiny(String sql, String parametr) throws SQLException, NamingException {
   1.192  		Collection<String> skupiny = new ArrayList<>();
   1.193  
   1.194  		Connection s = null;
   1.195  		PreparedStatement ps = null;
   1.196  		ResultSet rs = null;
   1.197  		try {
   1.198 -			s = datovýZdroj.getConnection();
   1.199 -			ps = s.prepareStatement(sqlSkupinyUživatele);
   1.200 -			ps.setString(1, uživatel);
   1.201 +			s = getSpojení();
   1.202 +
   1.203 +			ps = s.prepareStatement(sql);
   1.204 +			if (parametr != null) {
   1.205 +				ps.setString(1, parametr);
   1.206 +			}
   1.207  			rs = ps.executeQuery();
   1.208  
   1.209  			while (rs.next()) {
   1.210 @@ -99,18 +183,13 @@
   1.211  			}
   1.212  
   1.213  			return Collections.enumeration(skupiny);
   1.214 -
   1.215 -		} catch (Exception e) {
   1.216 -			String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + ".";
   1.217 -			_logger.log(Level.WARNING, hláška, e);
   1.218 -			throw new NoSuchUserException(hláška);
   1.219  		} finally {
   1.220  			zavri(s, ps, rs);
   1.221  		}
   1.222  	}
   1.223  
   1.224  	/**
   1.225 -	 * 
   1.226 +	 *
   1.227  	 * @param jméno uživatelské jméno
   1.228  	 * @param heslo heslo
   1.229  	 * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku
   1.230 @@ -122,7 +201,7 @@
   1.231  		PreparedStatement ps = null;
   1.232  		ResultSet rs = null;
   1.233  		try {
   1.234 -			s = datovýZdroj.getConnection();
   1.235 +			s = getSpojení();
   1.236  			ps = s.prepareStatement(sqlHeslo);
   1.237  			ps.setString(1, jméno);
   1.238  			ps.setString(2, new String(heslo));
   1.239 @@ -130,16 +209,16 @@
   1.240  
   1.241  			if (rs.next()) {
   1.242  				String dbJméno = rs.getString(1);
   1.243 -				if (dbJméno.equals(jméno)) {
   1.244 +				if (dbJméno != null && dbJméno.equals(jméno)) {
   1.245  					// OK – úspěšné ověření
   1.246  					return true;
   1.247  				} else {
   1.248 -					throw new LoginException("Nebyl nalezen správný uživatel: " + jméno + " != " + dbJméno);
   1.249 +					throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno + " != " + dbJméno);
   1.250  				}
   1.251  			} else {
   1.252 -				throw new LoginException("Uživatel nebyl nalezen: " + jméno);
   1.253 +				throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno);
   1.254  			}
   1.255 -		} catch (SQLException e) {
   1.256 +		} catch (NamingException | SQLException e) {
   1.257  			String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + ".";
   1.258  			_logger.log(Level.WARNING, hláška, e);
   1.259  			throw new LoginException(hláška);
   1.260 @@ -148,16 +227,6 @@
   1.261  		}
   1.262  	}
   1.263  
   1.264 -	private String najdiParametr(Properties parametry, String názevParametru, String popis) throws BadRealmException {
   1.265 -		String hodnotaParametru = parametry.getProperty(názevParametru);
   1.266 -
   1.267 -		if (hodnotaParametru == null || hodnotaParametru.length() < 1) {
   1.268 -			throw new BadRealmException("Chybí " + popis + " – parametr: " + názevParametru);
   1.269 -		} else {
   1.270 -			return hodnotaParametru;
   1.271 -		}
   1.272 -	}
   1.273 -
   1.274  	/**
   1.275  	 * Zavře všechno
   1.276  	 *
   1.277 @@ -170,18 +239,21 @@
   1.278  			try {
   1.279  				vysledek.close();
   1.280  			} catch (Exception e) {
   1.281 +				_logger.log(Level.FINE, "Při zavírání SQL výsledku došlo k chybě.", e);
   1.282  			}
   1.283  		}
   1.284  		if (prikaz != null) {
   1.285  			try {
   1.286  				prikaz.close();
   1.287  			} catch (Exception e) {
   1.288 +				_logger.log(Level.FINE, "Při zavírání SQL příkazu došlo k chybě.", e);
   1.289  			}
   1.290  		}
   1.291  		if (spojeni != null) {
   1.292  			try {
   1.293  				spojeni.close();
   1.294  			} catch (Exception e) {
   1.295 +				_logger.log(Level.FINE, "Při zavírání SQL spojení došlo k chybě.", e);
   1.296  			}
   1.297  		}
   1.298  	}