1.1 --- a/.hgignore Tue Feb 07 00:27:39 2012 +0100
1.2 +++ b/.hgignore Tue Feb 07 18:03:31 2012 +0100
1.3 @@ -1,3 +1,8 @@
1.4 +syntax: glob
1.5 +*~
1.6 java/sql-java-prihlasovani/build/*
1.7 java/sql-java-prihlasovani/dist/*
1.8 java/sql-java-prihlasovani/nbproject/private
1.9 +
1.10 +syntax: regexp
1.11 +
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/ParametrizovanýRealm.java Tue Feb 07 18:03:31 2012 +0100
2.3 @@ -0,0 +1,48 @@
2.4 +package cz.frantovo.jaas.sql;
2.5 +
2.6 +import com.sun.appserv.security.AppservRealm;
2.7 +import com.sun.enterprise.security.auth.realm.BadRealmException;
2.8 +import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
2.9 +import java.util.Properties;
2.10 +
2.11 +/**
2.12 + *
2.13 + * @author Ing. František Kučera (frantovo.cz)
2.14 + */
2.15 +public abstract class ParametrizovanýRealm extends AppservRealm {
2.16 +
2.17 + @Override
2.18 + protected void init(Properties parametry) throws BadRealmException, NoSuchRealmException {
2.19 + super.init(parametry);
2.20 + getProperties().putAll(parametry);
2.21 + parametrizuj();
2.22 + }
2.23 +
2.24 + @Override
2.25 + public synchronized void setProperty(String název, String hodnota) {
2.26 + super.setProperty(název, hodnota);
2.27 + try {
2.28 + parametrizuj();
2.29 + } catch (BadRealmException e) {
2.30 + throw new IllegalArgumentException("Tento parametr nelze nastavit: " + název + " = " + hodnota, e);
2.31 + }
2.32 + }
2.33 +
2.34 + /**
2.35 + * @param názevParametru který hledáme
2.36 + * @param popis pokud dojde k výjimce, tento popis se v ní objeví
2.37 + * @return hodnota parametru nebo…
2.38 + * @throws BadRealmException … pokud byl parametr nulový nebo po měl oříznutí nulovou délku
2.39 + */
2.40 + protected String najdiParametr(String názevParametru, String popis) throws BadRealmException {
2.41 + String hodnotaParametru = getProperty(názevParametru);
2.42 +
2.43 + if (hodnotaParametru == null || hodnotaParametru.trim().length() < 1) {
2.44 + throw new BadRealmException("Chybí " + popis + " – parametr: " + názevParametru);
2.45 + } else {
2.46 + return hodnotaParametru;
2.47 + }
2.48 + }
2.49 +
2.50 + protected abstract void parametrizuj() throws BadRealmException;
2.51 +}
3.1 --- a/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java Tue Feb 07 00:27:39 2012 +0100
3.2 +++ b/java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java Tue Feb 07 18:03:31 2012 +0100
3.3 @@ -1,8 +1,6 @@
3.4 package cz.frantovo.jaas.sql;
3.5
3.6 -import com.sun.appserv.security.AppservRealm;
3.7 import com.sun.enterprise.security.auth.realm.BadRealmException;
3.8 -import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
3.9 import com.sun.enterprise.security.auth.realm.NoSuchUserException;
3.10 import java.sql.Connection;
3.11 import java.sql.PreparedStatement;
3.12 @@ -13,7 +11,6 @@
3.13 import java.util.Collection;
3.14 import java.util.Collections;
3.15 import java.util.Enumeration;
3.16 -import java.util.Properties;
3.17 import java.util.logging.Level;
3.18 import javax.naming.InitialContext;
3.19 import javax.naming.NamingException;
3.20 @@ -25,52 +22,113 @@
3.21 *
3.22 * @author fiki
3.23 */
3.24 -public class SQLRealm extends AppservRealm {
3.25 +public class SQLRealm extends ParametrizovanýRealm {
3.26
3.27 private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi.";
3.28 - private static final String PARAM_JNDI = "jndi";
3.29 - private static final String PARAM_SQL_HESLO = "sql_heslo";
3.30 - private static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele";
3.31 - private static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny";
3.32 - private DataSource datovýZdroj;
3.33 + /**
3.34 + * <p>JNDI jméno datového zdroje – odkazuje na instanci {@linkplain javax.sql.DataSource}</p>
3.35 + *
3.36 + * <p>Příklady:</p>
3.37 + *
3.38 + * <ul>
3.39 + * <li><code>jdbc/mojeAplikace</code></li>
3.40 + * </ul>
3.41 + */
3.42 + public static final String PARAM_JNDI = "jndi";
3.43 + /** Pokud je nastaveno na "true", nebude se při inicializaci testovat spojení s databází */
3.44 + public static final String PARAM_NETESTOVAT_SPOJENÍ = "netestovat_spojeni";
3.45 + /**
3.46 + * <p>SQL dotaz pro kontrolu hesla.</p>
3.47 + *
3.48 + * <p>Má dva parametry:</p>
3.49 + * <ol>
3.50 + * <li>uživatelské jméno</li>
3.51 + * <li>heslo</li>
3.52 + * </ol>
3.53 + *
3.54 + * <p>Vrací:</p>
3.55 + * <ul>
3.56 + * <li>Při úspěšném ověření jednu hodnotu: uživatelské jméno</li>
3.57 + * <li>Při neexistujícím uživateli nebo špatném hesle: prázdný výsledek (nula řádků)</li>
3.58 + * <li>Při jiné chybě: vyhazuje výjimku</li>
3.59 + * </ul>
3.60 + *
3.61 + * <p>Příklady:</p>
3.62 + *
3.63 + * <ul>
3.64 + * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?</code></li>
3.65 + * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = sha1(?)</code></li>
3.66 + * <li><code>SELECT zkontroluj_heslo(?, ?)</code></li>
3.67 + * </ul>
3.68 + *
3.69 + * <p>(hashovací a jiné funkce musí být samozřejmě podporované databází)</p>
3.70 + */
3.71 + public static final String PARAM_SQL_HESLO = "sql_heslo";
3.72 + /**
3.73 + * <p>SQL dotaz pro zjištění skupin daného uživatele.</p>
3.74 + *
3.75 + * <p>Má jeden parametr: jméno uživatele</p>
3.76 + *
3.77 + * <p>Příklady:</p>
3.78 + *
3.79 + * <ul>
3.80 + * <li><code>SELECT nazev FROM skupina WHERE uzivatel = ?</code></li>
3.81 + * </ul>
3.82 + */
3.83 + public static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele";
3.84 + /**
3.85 + * <p>SQL dotaz pro zjištění všech skupin v dané bezpečnostní doméně.</p>
3.86 + *
3.87 + * <p>Příklady:</p>
3.88 + *
3.89 + * <ul>
3.90 + * <li><code>SELECT nazev FROM skupina</code></li>
3.91 + * </ul>
3.92 + */
3.93 + public static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny";
3.94 + private String jndiDatovéhoZdroje;
3.95 private String sqlHeslo;
3.96 private String sqlSkupinyUživatele;
3.97 private String sqlSkupinyVšechny;
3.98
3.99 - /**
3.100 - * Načteme a zkontrolujeme parametry
3.101 - *
3.102 - * @param parametry
3.103 - * @throws BadRealmException pokud je v parametrech chyba
3.104 - * @throws NoSuchRealmException
3.105 - */
3.106 @Override
3.107 - public void init(Properties parametry) throws BadRealmException, NoSuchRealmException {
3.108 - super.init(parametry);
3.109 -
3.110 - String jaasContext = parametry.getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT);
3.111 - setProperty(JAAS_CONTEXT_PARAM, jaasContext);
3.112 + protected void parametrizuj() throws BadRealmException {
3.113 + String jaasContext = getProperties().getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT);
3.114 + getProperties().setProperty(JAAS_CONTEXT_PARAM, jaasContext);
3.115
3.116 /** Databázové spojení */
3.117 - {
3.118 - String jndi = najdiParametr(parametry, PARAM_JNDI, "název datového zdroje");
3.119 + jndiDatovéhoZdroje = najdiParametr(PARAM_JNDI, "název datového zdroje");
3.120 + testSpojení();
3.121 +
3.122 + /** SQL dotazy */
3.123 + sqlHeslo = najdiParametr(PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla");
3.124 + sqlSkupinyUživatele = najdiParametr(PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele");
3.125 + sqlSkupinyVšechny = najdiParametr(PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin");
3.126 +
3.127 + _logger.log(Level.INFO, "SQLRealm úspěšně parametrizován. JaasContext: {0}, počet parametrů: {1}", new Object[]{getJAASContext(), getProperties().size()});
3.128 + }
3.129 +
3.130 + private void testSpojení() throws BadRealmException {
3.131 + if (Boolean.valueOf(getProperty(PARAM_NETESTOVAT_SPOJENÍ))) {
3.132 + _logger.log(Level.WARNING, "Netestujeme databázové spojení při inicializaci.");
3.133 + } else {
3.134 try {
3.135 - InitialContext k = new InitialContext();
3.136 - jndi = parametry.getProperty(PARAM_JNDI);
3.137 - datovýZdroj = (DataSource) k.lookup(jndi);
3.138 + Connection s = getSpojení();
3.139 + s.close();
3.140 } catch (NamingException e) {
3.141 - throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndi, e);
3.142 + throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndiDatovéhoZdroje, e);
3.143 + } catch (SQLException e) {
3.144 + throw new BadRealmException("Nepodařilo se navázat spojení s DB datového zdroje: " + jndiDatovéhoZdroje, e);
3.145 }
3.146 }
3.147 + }
3.148
3.149 - /** SQL dotazy */
3.150 - sqlHeslo = najdiParametr(parametry, PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla");
3.151 - sqlSkupinyUživatele = najdiParametr(parametry, PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele");
3.152 - sqlSkupinyVšechny = najdiParametr(parametry, PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin");
3.153 + private Connection getSpojení() throws NamingException, SQLException {
3.154 + InitialContext k = new InitialContext();
3.155 + DataSource datovýZdroj = (DataSource) k.lookup(jndiDatovéhoZdroje);
3.156 + return datovýZdroj.getConnection();
3.157 + }
3.158
3.159 - _logger.log(Level.INFO, "SQLRealm úspěšně vytvořen. JaasContext: {0}", jaasContext);
3.160 - }
3.161 -
3.162 @Override
3.163 public String getAuthType() {
3.164 return AUTH_TYPE;
3.165 @@ -83,15 +141,41 @@
3.166 */
3.167 @Override
3.168 public Enumeration<String> getGroupNames(String uživatel) throws NoSuchUserException {
3.169 + try {
3.170 + return getSkupiny(sqlSkupinyUživatele, uživatel);
3.171 + } catch (NamingException | SQLException e) {
3.172 + String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + ".";
3.173 + _logger.log(Level.WARNING, hláška, e);
3.174 + throw new NoSuchUserException(hláška);
3.175 + }
3.176 + }
3.177 +
3.178 + /**
3.179 + * @return seznam všech skupin v této bezpečnostní doméně
3.180 + * @throws BadRealmException v případě SQL chyby
3.181 + */
3.182 + @Override
3.183 + public Enumeration getGroupNames() throws BadRealmException {
3.184 + try {
3.185 + return getSkupiny(sqlSkupinyVšechny, null);
3.186 + } catch (NamingException | SQLException e) {
3.187 + throw new BadRealmException("Chyba při zjišťování seznamu všech skupin.", e);
3.188 + }
3.189 + }
3.190 +
3.191 + public Enumeration<String> getSkupiny(String sql, String parametr) throws SQLException, NamingException {
3.192 Collection<String> skupiny = new ArrayList<>();
3.193
3.194 Connection s = null;
3.195 PreparedStatement ps = null;
3.196 ResultSet rs = null;
3.197 try {
3.198 - s = datovýZdroj.getConnection();
3.199 - ps = s.prepareStatement(sqlSkupinyUživatele);
3.200 - ps.setString(1, uživatel);
3.201 + s = getSpojení();
3.202 +
3.203 + ps = s.prepareStatement(sql);
3.204 + if (parametr != null) {
3.205 + ps.setString(1, parametr);
3.206 + }
3.207 rs = ps.executeQuery();
3.208
3.209 while (rs.next()) {
3.210 @@ -99,18 +183,13 @@
3.211 }
3.212
3.213 return Collections.enumeration(skupiny);
3.214 -
3.215 - } catch (Exception e) {
3.216 - String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + ".";
3.217 - _logger.log(Level.WARNING, hláška, e);
3.218 - throw new NoSuchUserException(hláška);
3.219 } finally {
3.220 zavri(s, ps, rs);
3.221 }
3.222 }
3.223
3.224 /**
3.225 - *
3.226 + *
3.227 * @param jméno uživatelské jméno
3.228 * @param heslo heslo
3.229 * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku
3.230 @@ -122,7 +201,7 @@
3.231 PreparedStatement ps = null;
3.232 ResultSet rs = null;
3.233 try {
3.234 - s = datovýZdroj.getConnection();
3.235 + s = getSpojení();
3.236 ps = s.prepareStatement(sqlHeslo);
3.237 ps.setString(1, jméno);
3.238 ps.setString(2, new String(heslo));
3.239 @@ -130,16 +209,16 @@
3.240
3.241 if (rs.next()) {
3.242 String dbJméno = rs.getString(1);
3.243 - if (dbJméno.equals(jméno)) {
3.244 + if (dbJméno != null && dbJméno.equals(jméno)) {
3.245 // OK – úspěšné ověření
3.246 return true;
3.247 } else {
3.248 - throw new LoginException("Nebyl nalezen správný uživatel: " + jméno + " != " + dbJméno);
3.249 + throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno + " != " + dbJméno);
3.250 }
3.251 } else {
3.252 - throw new LoginException("Uživatel nebyl nalezen: " + jméno);
3.253 + throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno);
3.254 }
3.255 - } catch (SQLException e) {
3.256 + } catch (NamingException | SQLException e) {
3.257 String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + ".";
3.258 _logger.log(Level.WARNING, hláška, e);
3.259 throw new LoginException(hláška);
3.260 @@ -148,16 +227,6 @@
3.261 }
3.262 }
3.263
3.264 - private String najdiParametr(Properties parametry, String názevParametru, String popis) throws BadRealmException {
3.265 - String hodnotaParametru = parametry.getProperty(názevParametru);
3.266 -
3.267 - if (hodnotaParametru == null || hodnotaParametru.length() < 1) {
3.268 - throw new BadRealmException("Chybí " + popis + " – parametr: " + názevParametru);
3.269 - } else {
3.270 - return hodnotaParametru;
3.271 - }
3.272 - }
3.273 -
3.274 /**
3.275 * Zavře všechno
3.276 *
3.277 @@ -170,18 +239,21 @@
3.278 try {
3.279 vysledek.close();
3.280 } catch (Exception e) {
3.281 + _logger.log(Level.FINE, "Při zavírání SQL výsledku došlo k chybě.", e);
3.282 }
3.283 }
3.284 if (prikaz != null) {
3.285 try {
3.286 prikaz.close();
3.287 } catch (Exception e) {
3.288 + _logger.log(Level.FINE, "Při zavírání SQL příkazu došlo k chybě.", e);
3.289 }
3.290 }
3.291 if (spojeni != null) {
3.292 try {
3.293 spojeni.close();
3.294 } catch (Exception e) {
3.295 + _logger.log(Level.FINE, "Při zavírání SQL spojení došlo k chybě.", e);
3.296 }
3.297 }
3.298 }
4.1 --- a/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLLoginModulTest.java Tue Feb 07 00:27:39 2012 +0100
4.2 +++ b/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLLoginModulTest.java Tue Feb 07 18:03:31 2012 +0100
4.3 @@ -1,5 +1,6 @@
4.4 package cz.frantovo.jaas.sql;
4.5
4.6 +import javax.security.auth.login.LoginException;
4.7 import org.junit.Test;
4.8 import static org.junit.Assert.*;
4.9
4.10 @@ -9,4 +10,15 @@
4.11 */
4.12 public class SQLLoginModulTest {
4.13
4.14 + @Test(expected = LoginException.class)
4.15 + public void testŠpatnýRealm() throws LoginException {
4.16 + try {
4.17 + SQLLoginModul m = new SQLLoginModul();
4.18 + m.authenticateUser();
4.19 + } catch (LoginException e) {
4.20 + System.out.println("testŠpatnýRealm: " + e.getMessage());
4.21 + assertTrue(e.getMessage().startsWith("Špatný realm"));
4.22 + throw e;
4.23 + }
4.24 + }
4.25 }
5.1 --- a/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java Tue Feb 07 00:27:39 2012 +0100
5.2 +++ b/java/sql-java-prihlasovani/test/cz/frantovo/jaas/sql/SQLRealmTest.java Tue Feb 07 18:03:31 2012 +0100
5.3 @@ -3,6 +3,7 @@
5.4 import com.sun.enterprise.security.auth.realm.BadRealmException;
5.5 import com.sun.enterprise.security.auth.realm.NoSuchRealmException;
5.6 import java.util.Properties;
5.7 +import javax.naming.NamingException;
5.8 import org.junit.Before;
5.9 import org.junit.Test;
5.10 import static org.junit.Assert.*;
5.11 @@ -14,15 +15,30 @@
5.12 public class SQLRealmTest {
5.13
5.14 private static final String JAAS_KONTEXT = "sqlRealm_123456";
5.15 + private static final String JNDI_DS = "jdbc/test";
5.16 + private static final String SQL_HESLO = "SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?";
5.17 + private static final String SQL_SKUPINY_UŽIVATELE = "SELECT …";
5.18 + private static final String SQL_SKUPINY_VŠECHNY = "SELECT …";
5.19 + private static final String PARAMETR_NEPOVINNÝ = "nepovinný_parametr";
5.20 + private static final String PARAMETR_NEPOVINNÝ_HODNOTA = "hodnota nepovinného parametru";
5.21 private SQLRealm realm;
5.22
5.23 @Before
5.24 - public void setUp() throws BadRealmException, NoSuchRealmException {
5.25 + public void setUp() throws BadRealmException, NoSuchRealmException, NamingException {
5.26 Properties parametry = new Properties();
5.27 +
5.28 parametry.setProperty(SQLRealm.JAAS_CONTEXT_PARAM, JAAS_KONTEXT);
5.29 + parametry.setProperty(SQLRealm.PARAM_JNDI, JNDI_DS);
5.30 + parametry.setProperty(SQLRealm.PARAM_SQL_HESLO, SQL_HESLO);
5.31 + parametry.setProperty(SQLRealm.PARAM_SQL_SKUPINY_UŽIVATELE, SQL_SKUPINY_UŽIVATELE);
5.32 + parametry.setProperty(SQLRealm.PARAM_SQL_SKUPINY_VŠECHNY, SQL_SKUPINY_VŠECHNY);
5.33 +
5.34 + parametry.setProperty(SQLRealm.PARAM_NETESTOVAT_SPOJENÍ, Boolean.TRUE.toString());
5.35
5.36 realm = new SQLRealm();
5.37 realm.init(parametry);
5.38 +
5.39 + realm.setProperty(PARAMETR_NEPOVINNÝ, PARAMETR_NEPOVINNÝ_HODNOTA);
5.40 }
5.41
5.42 @Test
5.43 @@ -39,4 +55,12 @@
5.44 assertTrue("authType musí být nenulový", authType != null);
5.45 assertTrue("authType musí být neprázdný", authType.trim().length() > 0);
5.46 }
5.47 +
5.48 + @Test
5.49 + public void testGetProperty() {
5.50 + assertEquals("Neplatná hodnota parametru JAAS_KONTEXT", JAAS_KONTEXT, realm.getProperty(SQLRealm.JAAS_CONTEXT_PARAM));
5.51 + assertEquals("Neplatná hodnota parametru SQL_HESLO", SQL_HESLO, realm.getProperty(SQLRealm.PARAM_SQL_HESLO));
5.52 + assertEquals("Neplatná hodnota parametru PARAMETR_NEPOVINNÝ", PARAMETR_NEPOVINNÝ_HODNOTA, realm.getProperty(PARAMETR_NEPOVINNÝ));
5.53 +
5.54 + }
5.55 }