java/sql-java-prihlasovani/src/cz/frantovo/jaas/sql/SQLRealm.java
author František Kučera <franta-hg@frantovo.cz>
Tue, 07 Feb 2012 18:03:31 +0100
changeset 7 46bb283a674d
parent 6 aff44e80f418
permissions -rw-r--r--
uživatelé a skupiny v SQL databázi, parametrizace, hellDesk: #11
franta-hg@2
     1
package cz.frantovo.jaas.sql;
franta-hg@2
     2
franta-hg@4
     3
import com.sun.enterprise.security.auth.realm.BadRealmException;
franta-hg@2
     4
import com.sun.enterprise.security.auth.realm.NoSuchUserException;
franta-hg@6
     5
import java.sql.Connection;
franta-hg@6
     6
import java.sql.PreparedStatement;
franta-hg@6
     7
import java.sql.ResultSet;
franta-hg@6
     8
import java.sql.SQLException;
franta-hg@6
     9
import java.sql.Statement;
franta-hg@6
    10
import java.util.ArrayList;
franta-hg@6
    11
import java.util.Collection;
franta-hg@6
    12
import java.util.Collections;
franta-hg@2
    13
import java.util.Enumeration;
franta-hg@4
    14
import java.util.logging.Level;
franta-hg@6
    15
import javax.naming.InitialContext;
franta-hg@6
    16
import javax.naming.NamingException;
franta-hg@4
    17
import javax.security.auth.login.LoginException;
franta-hg@6
    18
import javax.sql.DataSource;
franta-hg@2
    19
franta-hg@2
    20
/**
franta-hg@6
    21
 * Bezpečnostní doména. Uživatelé jsou uloženi v SQL databázi.
franta-hg@6
    22
 *
franta-hg@2
    23
 * @author fiki
franta-hg@2
    24
 */
franta-hg@7
    25
public class SQLRealm extends ParametrizovanýRealm {
franta-hg@2
    26
franta-hg@4
    27
	private static final String AUTH_TYPE = "Ověřuje uživatele proti SQL databázi.";
franta-hg@7
    28
	/**
franta-hg@7
    29
	 * <p>JNDI jméno datového zdroje – odkazuje na instanci {@linkplain javax.sql.DataSource}</p>
franta-hg@7
    30
	 *
franta-hg@7
    31
	 * <p>Příklady:</p>
franta-hg@7
    32
	 *
franta-hg@7
    33
	 * <ul>
franta-hg@7
    34
	 * <li><code>jdbc/mojeAplikace</code></li>
franta-hg@7
    35
	 * </ul>
franta-hg@7
    36
	 */
franta-hg@7
    37
	public static final String PARAM_JNDI = "jndi";
franta-hg@7
    38
	/** Pokud je nastaveno na "true", nebude se při inicializaci testovat spojení s databází */
franta-hg@7
    39
	public static final String PARAM_NETESTOVAT_SPOJENÍ = "netestovat_spojeni";
franta-hg@7
    40
	/**
franta-hg@7
    41
	 * <p>SQL dotaz pro kontrolu hesla.</p>
franta-hg@7
    42
	 *
franta-hg@7
    43
	 * <p>Má dva parametry:</p>
franta-hg@7
    44
	 * <ol>
franta-hg@7
    45
	 * <li>uživatelské jméno</li>
franta-hg@7
    46
	 * <li>heslo</li>
franta-hg@7
    47
	 * </ol>
franta-hg@7
    48
	 *
franta-hg@7
    49
	 * <p>Vrací:</p>
franta-hg@7
    50
	 * <ul>
franta-hg@7
    51
	 * <li>Při úspěšném ověření jednu hodnotu: uživatelské jméno</li>
franta-hg@7
    52
	 * <li>Při neexistujícím uživateli nebo špatném hesle: prázdný výsledek (nula řádků)</li>
franta-hg@7
    53
	 * <li>Při jiné chybě: vyhazuje výjimku</li>
franta-hg@7
    54
	 * </ul>
franta-hg@7
    55
	 *
franta-hg@7
    56
	 * <p>Příklady:</p>
franta-hg@7
    57
	 *
franta-hg@7
    58
	 * <ul>
franta-hg@7
    59
	 * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = ?</code></li>
franta-hg@7
    60
	 * <li><code>SELECT jmeno FROM uzivatel WHERE jmeno = ? AND heslo = sha1(?)</code></li>
franta-hg@7
    61
	 * <li><code>SELECT zkontroluj_heslo(?, ?)</code></li>
franta-hg@7
    62
	 * </ul>
franta-hg@7
    63
	 *
franta-hg@7
    64
	 * <p>(hashovací a jiné funkce musí být samozřejmě podporované databází)</p>
franta-hg@7
    65
	 */
franta-hg@7
    66
	public static final String PARAM_SQL_HESLO = "sql_heslo";
franta-hg@7
    67
	/**
franta-hg@7
    68
	 * <p>SQL dotaz pro zjištění skupin daného uživatele.</p>
franta-hg@7
    69
	 *
franta-hg@7
    70
	 * <p>Má jeden parametr: jméno uživatele</p>
franta-hg@7
    71
	 *
franta-hg@7
    72
	 * <p>Příklady:</p>
franta-hg@7
    73
	 *
franta-hg@7
    74
	 * <ul>
franta-hg@7
    75
	 * <li><code>SELECT nazev FROM skupina WHERE uzivatel = ?</code></li>
franta-hg@7
    76
	 * </ul>
franta-hg@7
    77
	 */
franta-hg@7
    78
	public static final String PARAM_SQL_SKUPINY_UŽIVATELE = "sql_skupiny_uzivatele";
franta-hg@7
    79
	/**
franta-hg@7
    80
	 * <p>SQL dotaz pro zjištění všech skupin v dané bezpečnostní doméně.</p>
franta-hg@7
    81
	 *
franta-hg@7
    82
	 * <p>Příklady:</p>
franta-hg@7
    83
	 *
franta-hg@7
    84
	 * <ul>
franta-hg@7
    85
	 * <li><code>SELECT nazev FROM skupina</code></li>
franta-hg@7
    86
	 * </ul>
franta-hg@7
    87
	 */
franta-hg@7
    88
	public static final String PARAM_SQL_SKUPINY_VŠECHNY = "sql_skupiny_vsechny";
franta-hg@7
    89
	private String jndiDatovéhoZdroje;
franta-hg@6
    90
	private String sqlHeslo;
franta-hg@6
    91
	private String sqlSkupinyUživatele;
franta-hg@6
    92
	private String sqlSkupinyVšechny;
franta-hg@4
    93
franta-hg@2
    94
	@Override
franta-hg@7
    95
	protected void parametrizuj() throws BadRealmException {
franta-hg@7
    96
		String jaasContext = getProperties().getProperty(JAAS_CONTEXT_PARAM, SQLLoginModul.VÝCHOZÍ_JAAS_KONTEXT);
franta-hg@7
    97
		getProperties().setProperty(JAAS_CONTEXT_PARAM, jaasContext);
franta-hg@4
    98
franta-hg@6
    99
		/** Databázové spojení */
franta-hg@7
   100
		jndiDatovéhoZdroje = najdiParametr(PARAM_JNDI, "název datového zdroje");
franta-hg@7
   101
		testSpojení();
franta-hg@7
   102
franta-hg@7
   103
		/** SQL dotazy */
franta-hg@7
   104
		sqlHeslo = najdiParametr(PARAM_SQL_HESLO, "SQL dotaz pro ověření jména a hesla");
franta-hg@7
   105
		sqlSkupinyUživatele = najdiParametr(PARAM_SQL_SKUPINY_UŽIVATELE, "SQL dotaz pro zjištění skupin uživatele");
franta-hg@7
   106
		sqlSkupinyVšechny = najdiParametr(PARAM_SQL_SKUPINY_VŠECHNY, "SQL dotaz pro zjištění všech skupin");
franta-hg@7
   107
franta-hg@7
   108
		_logger.log(Level.INFO, "SQLRealm úspěšně parametrizován. JaasContext: {0}, počet parametrů: {1}", new Object[]{getJAASContext(), getProperties().size()});
franta-hg@7
   109
	}
franta-hg@7
   110
franta-hg@7
   111
	private void testSpojení() throws BadRealmException {
franta-hg@7
   112
		if (Boolean.valueOf(getProperty(PARAM_NETESTOVAT_SPOJENÍ))) {
franta-hg@7
   113
			_logger.log(Level.WARNING, "Netestujeme databázové spojení při inicializaci.");
franta-hg@7
   114
		} else {
franta-hg@6
   115
			try {
franta-hg@7
   116
				Connection s = getSpojení();
franta-hg@7
   117
				s.close();
franta-hg@6
   118
			} catch (NamingException e) {
franta-hg@7
   119
				throw new BadRealmException("Datový zdroj s tímto názvem nebyl nalezen: " + jndiDatovéhoZdroje, e);
franta-hg@7
   120
			} catch (SQLException e) {
franta-hg@7
   121
				throw new BadRealmException("Nepodařilo se navázat spojení s DB datového zdroje: " + jndiDatovéhoZdroje, e);
franta-hg@6
   122
			}
franta-hg@6
   123
		}
franta-hg@7
   124
	}
franta-hg@6
   125
franta-hg@7
   126
	private Connection getSpojení() throws NamingException, SQLException {
franta-hg@7
   127
		InitialContext k = new InitialContext();
franta-hg@7
   128
		DataSource datovýZdroj = (DataSource) k.lookup(jndiDatovéhoZdroje);
franta-hg@7
   129
		return datovýZdroj.getConnection();
franta-hg@7
   130
	}
franta-hg@6
   131
franta-hg@2
   132
	@Override
franta-hg@4
   133
	public String getAuthType() {
franta-hg@4
   134
		return AUTH_TYPE;
franta-hg@4
   135
	}
franta-hg@4
   136
franta-hg@4
   137
	/**
franta-hg@4
   138
	 * @param uživatel přihlašovací jméno uživatele
franta-hg@4
   139
	 * @return seznam skupin, ve kterých se daný uživatel nachází
franta-hg@4
   140
	 * @throws NoSuchUserException když uživatel s tímto jménem neexistuje.
franta-hg@4
   141
	 */
franta-hg@4
   142
	@Override
franta-hg@6
   143
	public Enumeration<String> getGroupNames(String uživatel) throws NoSuchUserException {
franta-hg@7
   144
		try {
franta-hg@7
   145
			return getSkupiny(sqlSkupinyUživatele, uživatel);
franta-hg@7
   146
		} catch (NamingException | SQLException e) {
franta-hg@7
   147
			String hláška = "Chyba při zjišťování skupin uživatele: " + uživatel + ".";
franta-hg@7
   148
			_logger.log(Level.WARNING, hláška, e);
franta-hg@7
   149
			throw new NoSuchUserException(hláška);
franta-hg@7
   150
		}
franta-hg@7
   151
	}
franta-hg@7
   152
franta-hg@7
   153
	/**
franta-hg@7
   154
	 * @return seznam všech skupin v této bezpečnostní doméně
franta-hg@7
   155
	 * @throws BadRealmException v případě SQL chyby
franta-hg@7
   156
	 */
franta-hg@7
   157
	@Override
franta-hg@7
   158
	public Enumeration getGroupNames() throws BadRealmException {
franta-hg@7
   159
		try {
franta-hg@7
   160
			return getSkupiny(sqlSkupinyVšechny, null);
franta-hg@7
   161
		} catch (NamingException | SQLException e) {
franta-hg@7
   162
			throw new BadRealmException("Chyba při zjišťování seznamu všech skupin.", e);
franta-hg@7
   163
		}
franta-hg@7
   164
	}
franta-hg@7
   165
franta-hg@7
   166
	public Enumeration<String> getSkupiny(String sql, String parametr) throws SQLException, NamingException {
franta-hg@6
   167
		Collection<String> skupiny = new ArrayList<>();
franta-hg@6
   168
franta-hg@6
   169
		Connection s = null;
franta-hg@6
   170
		PreparedStatement ps = null;
franta-hg@6
   171
		ResultSet rs = null;
franta-hg@6
   172
		try {
franta-hg@7
   173
			s = getSpojení();
franta-hg@7
   174
franta-hg@7
   175
			ps = s.prepareStatement(sql);
franta-hg@7
   176
			if (parametr != null) {
franta-hg@7
   177
				ps.setString(1, parametr);
franta-hg@7
   178
			}
franta-hg@6
   179
			rs = ps.executeQuery();
franta-hg@6
   180
franta-hg@6
   181
			while (rs.next()) {
franta-hg@6
   182
				skupiny.add(rs.getString(1));
franta-hg@6
   183
			}
franta-hg@6
   184
franta-hg@6
   185
			return Collections.enumeration(skupiny);
franta-hg@6
   186
		} finally {
franta-hg@6
   187
			zavri(s, ps, rs);
franta-hg@6
   188
		}
franta-hg@4
   189
	}
franta-hg@4
   190
franta-hg@4
   191
	/**
franta-hg@7
   192
	 *
franta-hg@4
   193
	 * @param jméno uživatelské jméno
franta-hg@4
   194
	 * @param heslo heslo
franta-hg@6
   195
	 * @return true pokud je jméno a heslo v pořádku | false nikdy – vyhazuje výjimku
franta-hg@6
   196
	 * @throws LoginException pokud je jméno nebo heslo neplatné nebo došlo k jiné chybě
franta-hg@4
   197
	 */
franta-hg@6
   198
	public boolean ověřUživatele(String jméno, char[] heslo) throws LoginException {
franta-hg@4
   199
franta-hg@6
   200
		Connection s = null;
franta-hg@6
   201
		PreparedStatement ps = null;
franta-hg@6
   202
		ResultSet rs = null;
franta-hg@6
   203
		try {
franta-hg@7
   204
			s = getSpojení();
franta-hg@6
   205
			ps = s.prepareStatement(sqlHeslo);
franta-hg@6
   206
			ps.setString(1, jméno);
franta-hg@6
   207
			ps.setString(2, new String(heslo));
franta-hg@6
   208
			rs = ps.executeQuery();
franta-hg@4
   209
franta-hg@6
   210
			if (rs.next()) {
franta-hg@6
   211
				String dbJméno = rs.getString(1);
franta-hg@7
   212
				if (dbJméno != null && dbJméno.equals(jméno)) {
franta-hg@6
   213
					// OK – úspěšné ověření
franta-hg@6
   214
					return true;
franta-hg@6
   215
				} else {
franta-hg@7
   216
					throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno + " != " + dbJméno);
franta-hg@6
   217
				}
franta-hg@6
   218
			} else {
franta-hg@7
   219
				throw new LoginException("Špatné heslo nebo neexistující uživatel: " + jméno);
franta-hg@6
   220
			}
franta-hg@7
   221
		} catch (NamingException | SQLException e) {
franta-hg@6
   222
			String hláška = "SQL chyba při ověřování hesla uživatele: " + jméno + ".";
franta-hg@6
   223
			_logger.log(Level.WARNING, hláška, e);
franta-hg@6
   224
			throw new LoginException(hláška);
franta-hg@6
   225
		} finally {
franta-hg@6
   226
			zavri(s, ps, rs);
franta-hg@6
   227
		}
franta-hg@6
   228
	}
franta-hg@6
   229
franta-hg@6
   230
	/**
franta-hg@6
   231
	 * Zavře všechno
franta-hg@6
   232
	 *
franta-hg@6
   233
	 * @param spojeni DB spojení
franta-hg@6
   234
	 * @param prikaz DB dotaz
franta-hg@6
   235
	 * @param vysledek DB výsledek
franta-hg@6
   236
	 */
franta-hg@6
   237
	private static void zavri(Connection spojeni, Statement prikaz, ResultSet vysledek) {
franta-hg@6
   238
		if (vysledek != null) {
franta-hg@6
   239
			try {
franta-hg@6
   240
				vysledek.close();
franta-hg@6
   241
			} catch (Exception e) {
franta-hg@7
   242
				_logger.log(Level.FINE, "Při zavírání SQL výsledku došlo k chybě.", e);
franta-hg@6
   243
			}
franta-hg@6
   244
		}
franta-hg@6
   245
		if (prikaz != null) {
franta-hg@6
   246
			try {
franta-hg@6
   247
				prikaz.close();
franta-hg@6
   248
			} catch (Exception e) {
franta-hg@7
   249
				_logger.log(Level.FINE, "Při zavírání SQL příkazu došlo k chybě.", e);
franta-hg@6
   250
			}
franta-hg@6
   251
		}
franta-hg@6
   252
		if (spojeni != null) {
franta-hg@6
   253
			try {
franta-hg@6
   254
				spojeni.close();
franta-hg@6
   255
			} catch (Exception e) {
franta-hg@7
   256
				_logger.log(Level.FINE, "Při zavírání SQL spojení došlo k chybě.", e);
franta-hg@6
   257
			}
franta-hg@4
   258
		}
franta-hg@2
   259
	}
franta-hg@2
   260
}