franta-hg@2: package cz.frantovo.jaas.sql; franta-hg@2: franta-hg@4: import com.sun.enterprise.security.auth.realm.BadRealmException; franta-hg@2: import com.sun.enterprise.security.auth.realm.NoSuchUserException; franta-hg@6: import java.sql.Connection; franta-hg@6: import java.sql.PreparedStatement; franta-hg@6: import java.sql.ResultSet; franta-hg@6: import java.sql.SQLException; franta-hg@6: import java.sql.Statement; franta-hg@6: import java.util.ArrayList; franta-hg@6: import java.util.Collection; franta-hg@6: import java.util.Collections; franta-hg@2: import java.util.Enumeration; franta-hg@4: import java.util.logging.Level; franta-hg@6: import javax.naming.InitialContext; franta-hg@6: import javax.naming.NamingException; franta-hg@4: import javax.security.auth.login.LoginException; franta-hg@6: import javax.sql.DataSource; franta-hg@2: franta-hg@2: /** franta-hg@6: * Bezpečnostní doména. Uživatelé jsou uloženi v SQL databázi. franta-hg@6: * franta-hg@2: * @author fiki franta-hg@2: */ franta-hg@7: public class SQLRealm extends ParametrizovanýRealm { franta-hg@2: franta-hg@4: private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi."; franta-hg@7: /** franta-hg@7: *

JNDI jméno datového zdroje – odkazuje na instanci {@linkplain javax.sql.DataSource}

franta-hg@7: * franta-hg@7: *

Příklady:

franta-hg@7: * franta-hg@7: * franta-hg@7: */ franta-hg@7: public static final String PARAM_JNDI = "jndi"; franta-hg@7: /** Pokud je nastaveno na "true", nebude se při inicializaci testovat spojení s databází */ franta-hg@7: public static final String PARAM_NETESTOVAT_SPOJENÍ = "netestovat_spojeni"; franta-hg@7: /** franta-hg@7: *

SQL dotaz pro kontrolu hesla.

franta-hg@7: * franta-hg@7: *

Má dva parametry:

franta-hg@7: *
    franta-hg@7: *
  1. uživatelské jméno
  2. franta-hg@7: *
  3. heslo
  4. franta-hg@7: *
franta-hg@7: * franta-hg@7: *

Vrací:

franta-hg@7: * franta-hg@7: * franta-hg@7: *

Příklady:

franta-hg@7: * franta-hg@7: * franta-hg@7: * franta-hg@7: *

(hashovací a jiné funkce musí být samozřejmě podporované databází)

franta-hg@7: */ franta-hg@7: public static final String PARAM_SQL_HESLO = "sql_heslo"; franta-hg@7: /** franta-hg@7: *

SQL dotaz pro zjištění skupin daného uživatele.

franta-hg@7: * franta-hg@7: *

Má jeden parametr: jméno uživatele

franta-hg@7: * franta-hg@7: *

Příklady:

franta-hg@7: * franta-hg@7: * franta-hg@7: */ franta-hg@7: public static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele"; franta-hg@7: /** franta-hg@7: *

SQL dotaz pro zjištění všech skupin v dané bezpečnostní doméně.

franta-hg@7: * franta-hg@7: *

Příklady:

franta-hg@7: * franta-hg@7: * franta-hg@7: */ franta-hg@7: public static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny"; franta-hg@7: private String jndiDatovéhoZdroje; franta-hg@6: private String sqlHeslo; franta-hg@6: private String sqlSkupinyUživatele; franta-hg@6: private String sqlSkupinyVšechny; franta-hg@4: franta-hg@2: @Override franta-hg@7: protected void parametrizuj() throws BadRealmException { franta-hg@7: String jaasContext = getProperties().getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT); franta-hg@7: getProperties().setProperty(JAAS_CONTEXT_PARAM, jaasContext); franta-hg@4: franta-hg@6: /** Databázové spojení */ franta-hg@7: jndiDatovéhoZdroje = najdiParametr(PARAM_JNDI, "název datového zdroje"); franta-hg@7: testSpojení(); franta-hg@7: franta-hg@7: /** SQL dotazy */ franta-hg@7: sqlHeslo = najdiParametr(PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla"); franta-hg@7: sqlSkupinyUživatele = najdiParametr(PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele"); franta-hg@7: sqlSkupinyVšechny = najdiParametr(PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin"); franta-hg@7: franta-hg@7: _logger.log(Level.INFO, "SQLRealm úspěšně parametrizován. JaasContext: {0}, počet parametrů: {1}", new Object[]{getJAASContext(), getProperties().size()}); franta-hg@7: } franta-hg@7: franta-hg@7: private void testSpojení() throws BadRealmException { franta-hg@7: if (Boolean.valueOf(getProperty(PARAM_NETESTOVAT_SPOJENÍ))) { franta-hg@7: _logger.log(Level.WARNING, "Netestujeme databázové spojení při inicializaci."); franta-hg@7: } else { franta-hg@6: try { franta-hg@7: Connection s = getSpojení(); franta-hg@7: s.close(); franta-hg@6: } catch (NamingException e) { franta-hg@7: throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndiDatovéhoZdroje, e); franta-hg@7: } catch (SQLException e) { franta-hg@7: throw new BadRealmException("Nepodařilo se navázat spojení s DB datového zdroje: " + jndiDatovéhoZdroje, e); franta-hg@6: } franta-hg@6: } franta-hg@7: } franta-hg@6: franta-hg@7: private Connection getSpojení() throws NamingException, SQLException { franta-hg@7: InitialContext k = new InitialContext(); franta-hg@7: DataSource datovýZdroj = (DataSource) k.lookup(jndiDatovéhoZdroje); franta-hg@7: return datovýZdroj.getConnection(); franta-hg@7: } franta-hg@6: franta-hg@2: @Override franta-hg@4: public String getAuthType() { franta-hg@4: return AUTH_TYPE; franta-hg@4: } franta-hg@4: franta-hg@4: /** franta-hg@4: * @param uživatel přihlašovací jméno uživatele franta-hg@4: * @return seznam skupin, ve kterých se daný uživatel nachází franta-hg@4: * @throws NoSuchUserException když uživatel s tímto jménem neexistuje. franta-hg@4: */ franta-hg@4: @Override franta-hg@6: public Enumeration getGroupNames(String uživatel) throws NoSuchUserException { franta-hg@7: try { franta-hg@7: return getSkupiny(sqlSkupinyUživatele, uživatel); franta-hg@7: } catch (NamingException | SQLException e) { franta-hg@7: String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + "."; franta-hg@7: _logger.log(Level.WARNING, hláška, e); franta-hg@7: throw new NoSuchUserException(hláška); franta-hg@7: } franta-hg@7: } franta-hg@7: franta-hg@7: /** franta-hg@7: * @return seznam všech skupin v této bezpečnostní doméně franta-hg@7: * @throws BadRealmException v případě SQL chyby franta-hg@7: */ franta-hg@7: @Override franta-hg@7: public Enumeration getGroupNames() throws BadRealmException { franta-hg@7: try { franta-hg@7: return getSkupiny(sqlSkupinyVšechny, null); franta-hg@7: } catch (NamingException | SQLException e) { franta-hg@7: throw new BadRealmException("Chyba při zjišťování seznamu všech skupin.", e); franta-hg@7: } franta-hg@7: } franta-hg@7: franta-hg@7: public Enumeration getSkupiny(String sql, String parametr) throws SQLException, NamingException { franta-hg@6: Collection skupiny = new ArrayList<>(); franta-hg@6: franta-hg@6: Connection s = null; franta-hg@6: PreparedStatement ps = null; franta-hg@6: ResultSet rs = null; franta-hg@6: try { franta-hg@7: s = getSpojení(); franta-hg@7: franta-hg@7: ps = s.prepareStatement(sql); franta-hg@7: if (parametr != null) { franta-hg@7: ps.setString(1, parametr); franta-hg@7: } franta-hg@6: rs = ps.executeQuery(); franta-hg@6: franta-hg@6: while (rs.next()) { franta-hg@6: skupiny.add(rs.getString(1)); franta-hg@6: } franta-hg@6: franta-hg@6: return Collections.enumeration(skupiny); franta-hg@6: } finally { franta-hg@6: zavri(s, ps, rs); franta-hg@6: } franta-hg@4: } franta-hg@4: franta-hg@4: /** franta-hg@7: * franta-hg@4: * @param jméno uživatelské jméno franta-hg@4: * @param heslo heslo franta-hg@6: * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku franta-hg@6: * @throws LoginException pokud je jméno nebo heslo neplatné nebo došlo k jiné chybě franta-hg@4: */ franta-hg@6: public boolean ověřUživatele(String jméno, char[] heslo) throws LoginException { franta-hg@4: franta-hg@6: Connection s = null; franta-hg@6: PreparedStatement ps = null; franta-hg@6: ResultSet rs = null; franta-hg@6: try { franta-hg@7: s = getSpojení(); franta-hg@6: ps = s.prepareStatement(sqlHeslo); franta-hg@6: ps.setString(1, jméno); franta-hg@6: ps.setString(2, new String(heslo)); franta-hg@6: rs = ps.executeQuery(); franta-hg@4: franta-hg@6: if (rs.next()) { franta-hg@6: String dbJméno = rs.getString(1); franta-hg@7: if (dbJméno != null && dbJméno.equals(jméno)) { franta-hg@6: // OK – úspěšné ověření franta-hg@6: return true; franta-hg@6: } else { franta-hg@7: throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno + " != " + dbJméno); franta-hg@6: } franta-hg@6: } else { franta-hg@7: throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno); franta-hg@6: } franta-hg@7: } catch (NamingException | SQLException e) { franta-hg@6: String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + "."; franta-hg@6: _logger.log(Level.WARNING, hláška, e); franta-hg@6: throw new LoginException(hláška); franta-hg@6: } finally { franta-hg@6: zavri(s, ps, rs); franta-hg@6: } franta-hg@6: } franta-hg@6: franta-hg@6: /** franta-hg@6: * Zavře všechno franta-hg@6: * franta-hg@6: * @param spojeni DB spojení franta-hg@6: * @param prikaz DB dotaz franta-hg@6: * @param vysledek DB výsledek franta-hg@6: */ franta-hg@6: private static void zavri(Connection spojeni, Statement prikaz, ResultSet vysledek) { franta-hg@6: if (vysledek != null) { franta-hg@6: try { franta-hg@6: vysledek.close(); franta-hg@6: } catch (Exception e) { franta-hg@7: _logger.log(Level.FINE, "Při zavírání SQL výsledku došlo k chybě.", e); franta-hg@6: } franta-hg@6: } franta-hg@6: if (prikaz != null) { franta-hg@6: try { franta-hg@6: prikaz.close(); franta-hg@6: } catch (Exception e) { franta-hg@7: _logger.log(Level.FINE, "Při zavírání SQL příkazu došlo k chybě.", e); franta-hg@6: } franta-hg@6: } franta-hg@6: if (spojeni != null) { franta-hg@6: try { franta-hg@6: spojeni.close(); franta-hg@6: } catch (Exception e) { franta-hg@7: _logger.log(Level.FINE, "Při zavírání SQL spojení došlo k chybě.", e); franta-hg@6: } franta-hg@4: } franta-hg@2: } franta-hg@2: }