# HG changeset patch # User František Kučera # Date 1328634211 -3600 # Node ID 46bb283a674d43f6775a2644019666609cc2ffff # Parent aff44e80f418a723f7075e2ef1564f012493d9e9 uživatelé a skupiny v SQL databázi, parametrizace, hellDesk: #11 diff -r aff44e80f418 -r 46bb283a674d .hgignore --- a/.hgignore Tue Feb 07 00:27:39 2012 +0100 +++ b/.hgignore Tue Feb 07 18:03:31 2012 +0100 @@ -1,3 +1,8 @@ +syntax: glob +*~ java/sql-java-prihlasovani/build/* java/sql-java-prihlasovani/dist/* java/sql-java-prihlasovani/nbproject/private + +syntax: regexp + diff -r aff44e80f418 -r 46bb283a674d java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/ParametrizovanýRealm.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/ParametrizovanýRealm.java Tue Feb 07 18:03:31 2012 +0100 @@ -0,0 +1,48 @@ +package cz.frantovo.jaas.sql; + +import com.sun.appserv.security.AppservRealm; +import com.sun.enterprise.security.auth.realm.BadRealmException; +import com.sun.enterprise.security.auth.realm.NoSuchRealmException; +import java.util.Properties; + +/** + * + * @author Ing. František Kučera (frantovo.cz) + */ +public abstract class ParametrizovanýRealm extends AppservRealm { + + @Override + protected void init(Properties parametry) throws BadRealmException, NoSuchRealmException { + super.init(parametry); + getProperties().putAll(parametry); + parametrizuj(); + } + + @Override + public synchronized void setProperty(String název, String hodnota) { + super.setProperty(název, hodnota); + try { + parametrizuj(); + } catch (BadRealmException e) { + throw new IllegalArgumentException("Tento parametr nelze nastavit: " + název + " = " + hodnota, e); + } + } + + /** + * @param názevParametru který hledáme + * @param popis pokud dojde k výjimce, tento popis se v ní objeví + * @return hodnota parametru nebo… + * @throws BadRealmException … pokud byl parametr nulový nebo po měl oříznutí nulovou délku + */ + protected String najdiParametr(String názevParametru, String popis) throws BadRealmException { + String hodnotaParametru = getProperty(názevParametru); + + if (hodnotaParametru == null || hodnotaParametru.trim().length() < 1) { + throw new BadRealmException("Chybí " + popis + " – parametr: " + názevParametru); + } else { + return hodnotaParametru; + } + } + + protected abstract void parametrizuj() throws BadRealmException; +} diff -r aff44e80f418 -r 46bb283a674d java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java --- a/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java Tue Feb 07 00:27:39 2012 +0100 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java Tue Feb 07 18:03:31 2012 +0100 @@ -1,8 +1,6 @@ package cz.frantovo.jaas.sql; -import com.sun.appserv.security.AppservRealm; import com.sun.enterprise.security.auth.realm.BadRealmException; -import com.sun.enterprise.security.auth.realm.NoSuchRealmException; import com.sun.enterprise.security.auth.realm.NoSuchUserException; import java.sql.Connection; import java.sql.PreparedStatement; @@ -13,7 +11,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Enumeration; -import java.util.Properties; import java.util.logging.Level; import javax.naming.InitialContext; import javax.naming.NamingException; @@ -25,52 +22,113 @@ * * @author fiki */ -public class SQLRealm extends AppservRealm { +public class SQLRealm extends ParametrizovanýRealm { private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi."; - private static final String PARAM_JNDI = "jndi"; - private static final String PARAM_SQL_HESLO = "sql_heslo"; - private static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele"; - private static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny"; - private DataSource datovýZdroj; + /** + *

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

+ * + *

Příklady:

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

SQL dotaz pro kontrolu hesla.

+ * + *

Má dva parametry:

+ *
    + *
  1. uživatelské jméno
  2. + *
  3. heslo
  4. + *
+ * + *

Vrací:

+ * + * + *

Příklady:

+ * + * + * + *

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

+ */ + public static final String PARAM_SQL_HESLO = "sql_heslo"; + /** + *

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

+ * + *

Má jeden parametr: jméno uživatele

+ * + *

Příklady:

+ * + * + */ + public static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele"; + /** + *

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

+ * + *

Příklady:

+ * + * + */ + public static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny"; + private String jndiDatovéhoZdroje; private String sqlHeslo; private String sqlSkupinyUživatele; private String sqlSkupinyVšechny; - /** - * Načteme a zkontrolujeme parametry - * - * @param parametry - * @throws BadRealmException pokud je v parametrech chyba - * @throws NoSuchRealmException - */ @Override - public void init(Properties parametry) throws BadRealmException, NoSuchRealmException { - super.init(parametry); - - String jaasContext = parametry.getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT); - setProperty(JAAS_CONTEXT_PARAM, jaasContext); + protected void parametrizuj() throws BadRealmException { + String jaasContext = getProperties().getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT); + getProperties().setProperty(JAAS_CONTEXT_PARAM, jaasContext); /** Databázové spojení */ - { - String jndi = najdiParametr(parametry, PARAM_JNDI, "název datového zdroje"); + jndiDatovéhoZdroje = najdiParametr(PARAM_JNDI, "název datového zdroje"); + testSpojení(); + + /** SQL dotazy */ + sqlHeslo = najdiParametr(PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla"); + sqlSkupinyUživatele = najdiParametr(PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele"); + sqlSkupinyVšechny = najdiParametr(PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin"); + + _logger.log(Level.INFO, "SQLRealm úspěšně parametrizován. JaasContext: {0}, počet parametrů: {1}", new Object[]{getJAASContext(), getProperties().size()}); + } + + private void testSpojení() throws BadRealmException { + if (Boolean.valueOf(getProperty(PARAM_NETESTOVAT_SPOJENÍ))) { + _logger.log(Level.WARNING, "Netestujeme databázové spojení při inicializaci."); + } else { try { - InitialContext k = new InitialContext(); - jndi = parametry.getProperty(PARAM_JNDI); - datovýZdroj = (DataSource) k.lookup(jndi); + Connection s = getSpojení(); + s.close(); } catch (NamingException e) { - throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndi, e); + throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndiDatovéhoZdroje, e); + } catch (SQLException e) { + throw new BadRealmException("Nepodařilo se navázat spojení s DB datového zdroje: " + jndiDatovéhoZdroje, e); } } + } - /** SQL dotazy */ - sqlHeslo = najdiParametr(parametry, PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla"); - sqlSkupinyUživatele = najdiParametr(parametry, PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele"); - sqlSkupinyVšechny = najdiParametr(parametry, PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin"); + private Connection getSpojení() throws NamingException, SQLException { + InitialContext k = new InitialContext(); + DataSource datovýZdroj = (DataSource) k.lookup(jndiDatovéhoZdroje); + return datovýZdroj.getConnection(); + } - _logger.log(Level.INFO, "SQLRealm úspěšně vytvořen. JaasContext: {0}", jaasContext); - } - @Override public String getAuthType() { return AUTH_TYPE; @@ -83,15 +141,41 @@ */ @Override public Enumeration getGroupNames(String uživatel) throws NoSuchUserException { + try { + return getSkupiny(sqlSkupinyUživatele, uživatel); + } catch (NamingException | SQLException e) { + String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + "."; + _logger.log(Level.WARNING, hláška, e); + throw new NoSuchUserException(hláška); + } + } + + /** + * @return seznam všech skupin v této bezpečnostní doméně + * @throws BadRealmException v případě SQL chyby + */ + @Override + public Enumeration getGroupNames() throws BadRealmException { + try { + return getSkupiny(sqlSkupinyVšechny, null); + } catch (NamingException | SQLException e) { + throw new BadRealmException("Chyba při zjišťování seznamu všech skupin.", e); + } + } + + public Enumeration getSkupiny(String sql, String parametr) throws SQLException, NamingException { Collection skupiny = new ArrayList<>(); Connection s = null; PreparedStatement ps = null; ResultSet rs = null; try { - s = datovýZdroj.getConnection(); - ps = s.prepareStatement(sqlSkupinyUživatele); - ps.setString(1, uživatel); + s = getSpojení(); + + ps = s.prepareStatement(sql); + if (parametr != null) { + ps.setString(1, parametr); + } rs = ps.executeQuery(); while (rs.next()) { @@ -99,18 +183,13 @@ } return Collections.enumeration(skupiny); - - } catch (Exception e) { - String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + "."; - _logger.log(Level.WARNING, hláška, e); - throw new NoSuchUserException(hláška); } finally { zavri(s, ps, rs); } } /** - * + * * @param jméno uživatelské jméno * @param heslo heslo * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku @@ -122,7 +201,7 @@ PreparedStatement ps = null; ResultSet rs = null; try { - s = datovýZdroj.getConnection(); + s = getSpojení(); ps = s.prepareStatement(sqlHeslo); ps.setString(1, jméno); ps.setString(2, new String(heslo)); @@ -130,16 +209,16 @@ if (rs.next()) { String dbJméno = rs.getString(1); - if (dbJméno.equals(jméno)) { + if (dbJméno != null && dbJméno.equals(jméno)) { // OK – úspěšné ověření return true; } else { - throw new LoginException("Nebyl nalezen správný uživatel: " + jméno + " != " + dbJméno); + throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno + " != " + dbJméno); } } else { - throw new LoginException("Uživatel nebyl nalezen: " + jméno); + throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno); } - } catch (SQLException e) { + } catch (NamingException | SQLException e) { String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + "."; _logger.log(Level.WARNING, hláška, e); throw new LoginException(hláška); @@ -148,16 +227,6 @@ } } - private String najdiParametr(Properties parametry, String názevParametru, String popis) throws BadRealmException { - String hodnotaParametru = parametry.getProperty(názevParametru); - - if (hodnotaParametru == null || hodnotaParametru.length() < 1) { - throw new BadRealmException("Chybí " + popis + " – parametr: " + názevParametru); - } else { - return hodnotaParametru; - } - } - /** * Zavře všechno * @@ -170,18 +239,21 @@ try { vysledek.close(); } catch (Exception e) { + _logger.log(Level.FINE, "Při zavírání SQL výsledku došlo k chybě.", e); } } if (prikaz != null) { try { prikaz.close(); } catch (Exception e) { + _logger.log(Level.FINE, "Při zavírání SQL příkazu došlo k chybě.", e); } } if (spojeni != null) { try { spojeni.close(); } catch (Exception e) { + _logger.log(Level.FINE, "Při zavírání SQL spojení došlo k chybě.", e); } } } diff -r aff44e80f418 -r 46bb283a674d java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLLoginModulTest.java --- a/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLLoginModulTest.java Tue Feb 07 00:27:39 2012 +0100 +++ b/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLLoginModulTest.java Tue Feb 07 18:03:31 2012 +0100 @@ -1,5 +1,6 @@ package cz.frantovo.jaas.sql; +import javax.security.auth.login.LoginException; import org.junit.Test; import static org.junit.Assert.*; @@ -9,4 +10,15 @@ */ public class SQLLoginModulTest { + @Test(expected = LoginException.class) + public void testŠpatnýRealm() throws LoginException { + try { + SQLLoginModul m = new SQLLoginModul(); + m.authenticateUser(); + } catch (LoginException e) { + System.out.println("testŠpatnýRealm: " + e.getMessage()); + assertTrue(e.getMessage().startsWith("Špatný realm")); + throw e; + } + } } diff -r aff44e80f418 -r 46bb283a674d java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java --- a/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java Tue Feb 07 00:27:39 2012 +0100 +++ b/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java Tue Feb 07 18:03:31 2012 +0100 @@ -3,6 +3,7 @@ import com.sun.enterprise.security.auth.realm.BadRealmException; import com.sun.enterprise.security.auth.realm.NoSuchRealmException; import java.util.Properties; +import javax.naming.NamingException; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; @@ -14,15 +15,30 @@ public class SQLRealmTest { private static final String JAAS_KONTEXT = "sqlRealm_123456"; + private static final String JNDI_DS = "jdbc/test"; + private static final String SQL_HESLO = "SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?"; + private static final String SQL_SKUPINY_UŽIVATELE = "SELECT …"; + private static final String SQL_SKUPINY_VŠECHNY = "SELECT …"; + private static final String PARAMETR_NEPOVINNÝ = "nepovinný_parametr"; + private static final String PARAMETR_NEPOVINNÝ_HODNOTA = "hodnota nepovinného parametru"; private SQLRealm realm; @Before - public void setUp() throws BadRealmException, NoSuchRealmException { + public void setUp() throws BadRealmException, NoSuchRealmException, NamingException { Properties parametry = new Properties(); + parametry.setProperty(SQLRealm.JAAS_CONTEXT_PARAM, JAAS_KONTEXT); + parametry.setProperty(SQLRealm.PARAM_JNDI, JNDI_DS); + parametry.setProperty(SQLRealm.PARAM_SQL_HESLO, SQL_HESLO); + parametry.setProperty(SQLRealm.PARAM_SQL_SKUPINY_UŽIVATELE, SQL_SKUPINY_UŽIVATELE); + parametry.setProperty(SQLRealm.PARAM_SQL_SKUPINY_VŠECHNY, SQL_SKUPINY_VŠECHNY); + + parametry.setProperty(SQLRealm.PARAM_NETESTOVAT_SPOJENÍ, Boolean.TRUE.toString()); realm = new SQLRealm(); realm.init(parametry); + + realm.setProperty(PARAMETR_NEPOVINNÝ, PARAMETR_NEPOVINNÝ_HODNOTA); } @Test @@ -39,4 +55,12 @@ assertTrue("authType musí být nenulový", authType != null); assertTrue("authType musí být neprázdný", authType.trim().length() > 0); } + + @Test + public void testGetProperty() { + assertEquals("Neplatná hodnota parametru JAAS_KONTEXT", JAAS_KONTEXT, realm.getProperty(SQLRealm.JAAS_CONTEXT_PARAM)); + assertEquals("Neplatná hodnota parametru SQL_HESLO", SQL_HESLO, realm.getProperty(SQLRealm.PARAM_SQL_HESLO)); + assertEquals("Neplatná hodnota parametru PARAMETR_NEPOVINNÝ", PARAMETR_NEPOVINNÝ_HODNOTA, realm.getProperty(PARAMETR_NEPOVINNÝ)); + + } }