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: * jdbc/mojeAplikace
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: * - uživatelské jméno
franta-hg@7: * - heslo
franta-hg@7: *
franta-hg@7: *
franta-hg@7: * Vrací:
franta-hg@7: *
franta-hg@7: * - Při úspěšném ověření jednu hodnotu: uživatelské jméno
franta-hg@7: * - Při neexistujícím uživateli nebo špatném hesle: prázdný výsledek (nula řádků)
franta-hg@7: * - Při jiné chybě: vyhazuje výjimku
franta-hg@7: *
franta-hg@7: *
franta-hg@7: * Příklady:
franta-hg@7: *
franta-hg@7: *
franta-hg@7: * SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?
franta-hg@7: * SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = sha1(?)
franta-hg@7: * SELECT zkontroluj_heslo(?, ?)
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: * SELECT nazev FROM skupina WHERE uzivatel = ?
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: * SELECT nazev FROM skupina
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: }