1 package cz.frantovo.jaas.sql;
3 import com.sun.enterprise.security.auth.realm.BadRealmException;
4 import com.sun.enterprise.security.auth.realm.NoSuchUserException;
5 import java.sql.Connection;
6 import java.sql.PreparedStatement;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.sql.Statement;
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.Enumeration;
14 import java.util.logging.Level;
15 import javax.naming.InitialContext;
16 import javax.naming.NamingException;
17 import javax.security.auth.login.LoginException;
18 import javax.sql.DataSource;
21 * Bezpečnostní doména. Uživatelé jsou uloženi v SQL databázi.
25 public class SQLRealm extends ParametrizovanýRealm {
27 private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi.";
29 * <p>JNDI jméno datového zdroje – odkazuje na instanci {@linkplain javax.sql.DataSource}</p>
34 * <li><code>jdbc/mojeAplikace</code></li>
37 public static final String PARAM_JNDI = "jndi";
38 /** Pokud je nastaveno na "true", nebude se při inicializaci testovat spojení s databází */
39 public static final String PARAM_NETESTOVAT_SPOJENÍ = "netestovat_spojeni";
41 * <p>SQL dotaz pro kontrolu hesla.</p>
43 * <p>Má dva parametry:</p>
45 * <li>uživatelské jméno</li>
51 * <li>Při úspěšném ověření jednu hodnotu: uživatelské jméno</li>
52 * <li>Při neexistujícím uživateli nebo špatném hesle: prázdný výsledek (nula řádků)</li>
53 * <li>Při jiné chybě: vyhazuje výjimku</li>
59 * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?</code></li>
60 * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = sha1(?)</code></li>
61 * <li><code>SELECT zkontroluj_heslo(?, ?)</code></li>
64 * <p>(hashovací a jiné funkce musí být samozřejmě podporované databází)</p>
66 public static final String PARAM_SQL_HESLO = "sql_heslo";
68 * <p>SQL dotaz pro zjištění skupin daného uživatele.</p>
70 * <p>Má jeden parametr: jméno uživatele</p>
75 * <li><code>SELECT nazev FROM skupina WHERE uzivatel = ?</code></li>
78 public static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele";
80 * <p>SQL dotaz pro zjištění všech skupin v dané bezpečnostní doméně.</p>
85 * <li><code>SELECT nazev FROM skupina</code></li>
88 public static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny";
89 private String jndiDatovéhoZdroje;
90 private String sqlHeslo;
91 private String sqlSkupinyUživatele;
92 private String sqlSkupinyVšechny;
95 protected void parametrizuj() throws BadRealmException {
96 String jaasContext = getProperties().getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT);
97 getProperties().setProperty(JAAS_CONTEXT_PARAM, jaasContext);
99 /** Databázové spojení */
100 jndiDatovéhoZdroje = najdiParametr(PARAM_JNDI, "název datového zdroje");
104 sqlHeslo = najdiParametr(PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla");
105 sqlSkupinyUživatele = najdiParametr(PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele");
106 sqlSkupinyVšechny = najdiParametr(PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin");
108 _logger.log(Level.INFO, "SQLRealm úspěšně parametrizován. JaasContext: {0}, počet parametrů: {1}", new Object[]{getJAASContext(), getProperties().size()});
111 private void testSpojení() throws BadRealmException {
112 if (Boolean.valueOf(getProperty(PARAM_NETESTOVAT_SPOJENÍ))) {
113 _logger.log(Level.WARNING, "Netestujeme databázové spojení při inicializaci.");
116 Connection s = getSpojení();
118 } catch (NamingException e) {
119 throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndiDatovéhoZdroje, e);
120 } catch (SQLException e) {
121 throw new BadRealmException("Nepodařilo se navázat spojení s DB datového zdroje: " + jndiDatovéhoZdroje, e);
126 private Connection getSpojení() throws NamingException, SQLException {
127 InitialContext k = new InitialContext();
128 DataSource datovýZdroj = (DataSource) k.lookup(jndiDatovéhoZdroje);
129 return datovýZdroj.getConnection();
133 public String getAuthType() {
138 * @param uživatel přihlašovací jméno uživatele
139 * @return seznam skupin, ve kterých se daný uživatel nachází
140 * @throws NoSuchUserException když uživatel s tímto jménem neexistuje.
143 public Enumeration<String> getGroupNames(String uživatel) throws NoSuchUserException {
145 return getSkupiny(sqlSkupinyUživatele, uživatel);
146 } catch (NamingException | SQLException e) {
147 String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + ".";
148 _logger.log(Level.WARNING, hláška, e);
149 throw new NoSuchUserException(hláška);
154 * @return seznam všech skupin v této bezpečnostní doméně
155 * @throws BadRealmException v případě SQL chyby
158 public Enumeration getGroupNames() throws BadRealmException {
160 return getSkupiny(sqlSkupinyVšechny, null);
161 } catch (NamingException | SQLException e) {
162 throw new BadRealmException("Chyba při zjišťování seznamu všech skupin.", e);
166 public Enumeration<String> getSkupiny(String sql, String parametr) throws SQLException, NamingException {
167 Collection<String> skupiny = new ArrayList<>();
170 PreparedStatement ps = null;
175 ps = s.prepareStatement(sql);
176 if (parametr != null) {
177 ps.setString(1, parametr);
179 rs = ps.executeQuery();
182 skupiny.add(rs.getString(1));
185 return Collections.enumeration(skupiny);
193 * @param jméno uživatelské jméno
195 * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku
196 * @throws LoginException pokud je jméno nebo heslo neplatné nebo došlo k jiné chybě
198 public boolean ověřUživatele(String jméno, char[] heslo) throws LoginException {
201 PreparedStatement ps = null;
205 ps = s.prepareStatement(sqlHeslo);
206 ps.setString(1, jméno);
207 ps.setString(2, new String(heslo));
208 rs = ps.executeQuery();
211 String dbJméno = rs.getString(1);
212 if (dbJméno != null && dbJméno.equals(jméno)) {
213 // OK – úspěšné ověření
216 throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno + " != " + dbJméno);
219 throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno);
221 } catch (NamingException | SQLException e) {
222 String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + ".";
223 _logger.log(Level.WARNING, hláška, e);
224 throw new LoginException(hláška);
233 * @param spojeni DB spojení
234 * @param prikaz DB dotaz
235 * @param vysledek DB výsledek
237 private static void zavri(Connection spojeni, Statement prikaz, ResultSet vysledek) {
238 if (vysledek != null) {
241 } catch (Exception e) {
242 _logger.log(Level.FINE, "Při zavírání SQL výsledku došlo k chybě.", e);
245 if (prikaz != null) {
248 } catch (Exception e) {
249 _logger.log(Level.FINE, "Při zavírání SQL příkazu došlo k chybě.", e);
252 if (spojeni != null) {
255 } catch (Exception e) {
256 _logger.log(Level.FINE, "Při zavírání SQL spojení došlo k chybě.", e);