This commit is contained in:
UnlegitDqrk
2026-02-11 23:22:20 +01:00
parent 9483c36a66
commit 64ce55ea7b
39 changed files with 2033 additions and 3089 deletions

View File

@@ -0,0 +1,490 @@
package ins.frontend.utils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* Registrar DAO for schema:
* <ul>
* <li>tln(id, name, info, owner_id, is_public, allow_subdomains)</li>
* <li>infonames(id, info, tln_id, uid)</li>
* <li>subnames(id, name, infoname_id)</li>
* <li>records(id, infoname_id, subname_id, type, value, ttl, priority, port, weight)</li>
* </ul>
*
* <p>Rules:</p>
* <ul>
* <li>Create InfoName under TLN allowed if TLN is public OR owned by user.</li>
* <li>Subnames allowed for non-owner only if allow_subdomains=1. Owner always allowed.</li>
* <li>TLN name is not editable; only info/is_public/allow_subdomains are editable (owner-only).</li>
* </ul>
*/
public final class RegistrarDao {
private final UserDao.DataSourceProvider dataSource;
/**
* Creates the DAO.
*
* @param dataSource JDBC connection provider
*/
public RegistrarDao(UserDao.DataSourceProvider dataSource) {
this.dataSource = Objects.requireNonNull(dataSource, "dataSource");
}
// ---------- TLN ----------
/**
* TLN usable for creating InfoNames if public OR owned.
*
* @param tln TLN row
* @param userId current user id
* @return allowed
*/
public static boolean canUseTln(TlnRow tln, int userId) {
if (tln == null || userId <= 0) return false;
if (tln.isPublic) return true;
return tln.ownerId != null && tln.ownerId == userId;
}
/**
* Subdomain/subname allowed if:
* <ul>
* <li>no sub requested (root) OR</li>
* <li>TLN allows subdomains OR</li>
* <li>user is TLN owner</li>
* </ul>
*
* @param tln TLN row
* @param userId current user id
* @param sub sub label (nullable)
* @return allowed
*/
public static boolean canUseSubname(TlnRow tln, int userId, String sub) {
if (tln == null || userId <= 0) return false;
boolean wantsSub = sub != null && !sub.isBlank();
if (!wantsSub) return true;
if (tln.allowSubdomains) return true;
return tln.ownerId != null && tln.ownerId == userId;
}
private static TlnRow mapTln(ResultSet rs) throws SQLException {
return new TlnRow(
rs.getInt("id"),
rs.getString("name"),
rs.getString("info"),
(Integer) rs.getObject("owner_id"),
rs.getInt("is_public") == 1,
rs.getInt("allow_subdomains") == 1
);
}
/**
* Creates a TLN.
*
* @param name tln.name (unique)
* @param info tln.info (editable, can be null/blank)
* @param ownerId owner user id (nullable in DB, pass null to create unowned TLN)
* @param isPublic tln.is_public
* @param allowSubdomains tln.allow_subdomains
* @return new tln.id
* @throws SQLException on SQL errors
*/
public int createTln(String name, String info, Integer ownerId, boolean isPublic, boolean allowSubdomains) throws SQLException {
if (name == null || name.isBlank()) throw new IllegalArgumentException("name must not be blank");
String sql = "INSERT INTO tln (name, info, owner_id, is_public, allow_subdomains) VALUES (?, ?, ?, ?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, name.trim());
ps.setString(2, info);
if (ownerId == null) ps.setNull(3, Types.INTEGER);
else ps.setInt(3, ownerId);
ps.setInt(4, isPublic ? 1 : 0);
ps.setInt(5, allowSubdomains ? 1 : 0);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for tln.id");
}
/**
* Finds TLN by name.
*
* @param name tln.name
* @return optional row
* @throws SQLException on SQL errors
*/
public Optional<TlnRow> findTlnByName(String name) throws SQLException {
if (name == null || name.isBlank()) return Optional.empty();
String sql = "SELECT id, name, info, owner_id, is_public, allow_subdomains FROM tln WHERE name = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, name.trim());
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(mapTln(rs));
}
}
}
// ---------- Permissions ----------
/**
* Lists TLNs visible to a user: owned OR public OR unowned+public.
*
* @param userId current user id
* @return rows
* @throws SQLException on SQL errors
*/
public TlnRow[] listVisibleTlns(int userId) throws SQLException {
String sql = """
SELECT id, name, info, owner_id, is_public, allow_subdomains
FROM tln
WHERE is_public = 1 OR owner_id = ?
ORDER BY name ASC
""";
List<TlnRow> out = new ArrayList<>();
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) out.add(mapTln(rs));
}
}
return out.toArray(new TlnRow[0]);
}
/**
* Updates TLN fields except name (owner-only).
*
* @param tlnId tln.id
* @param ownerUserId users.id (must match tln.owner_id)
* @param newInfo new info text (nullable)
* @param isPublic is_public
* @param allowSubdomains allow_subdomains
* @return true if updated, false if not owned/not found
* @throws SQLException on SQL errors
*/
public boolean updateTlnOwned(int tlnId, int ownerUserId, String newInfo, boolean isPublic, boolean allowSubdomains) throws SQLException {
if (tlnId <= 0) throw new IllegalArgumentException("tlnId must be > 0");
if (ownerUserId <= 0) throw new IllegalArgumentException("ownerUserId must be > 0");
String sql = "UPDATE tln SET info = ?, is_public = ?, allow_subdomains = ? WHERE id = ? AND owner_id = ?";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, newInfo);
ps.setInt(2, isPublic ? 1 : 0);
ps.setInt(3, allowSubdomains ? 1 : 0);
ps.setInt(4, tlnId);
ps.setInt(5, ownerUserId);
return ps.executeUpdate() > 0;
}
}
// ---------- InfoNames ----------
/**
* Deletes TLN (owner-only).
*
* @param tlnId tln.id
* @param ownerUserId users.id
* @return true if deleted
* @throws SQLException on SQL errors
*/
public boolean deleteTlnOwned(int tlnId, int ownerUserId) throws SQLException {
if (tlnId <= 0) throw new IllegalArgumentException("tlnId must be > 0");
if (ownerUserId <= 0) throw new IllegalArgumentException("ownerUserId must be > 0");
String sql = "DELETE FROM tln WHERE id = ? AND owner_id = ?";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, tlnId);
ps.setInt(2, ownerUserId);
return ps.executeUpdate() > 0;
}
}
/**
* Creates an InfoName under the given TLN.
*
* @param tln TLN row
* @param info infonames.info
* @param userId infonames.uid (users.id)
* @return new infonames.id
* @throws SQLException on SQL errors
*/
public int createInfoName(TlnRow tln, String info, int userId) throws SQLException {
if (tln == null) throw new IllegalArgumentException("tln must not be null");
if (userId <= 0) throw new IllegalArgumentException("userId must be > 0");
if (!canUseTln(tln, userId)) throw new SQLException("TLN not public and not owned by user.");
if (info == null || info.isBlank()) throw new IllegalArgumentException("info must not be blank");
String sql = "INSERT INTO infonames (info, tln_id, uid) VALUES (?, ?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, info.trim());
ps.setInt(2, tln.id);
ps.setInt(3, userId);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for infonames.id");
}
/**
* Checks whether a user owns the infoname.
*
* @param infonameId infonames.id
* @param userId users.id
* @return true if owned
* @throws SQLException on SQL errors
*/
public boolean isOwnerOfInfoName(int infonameId, int userId) throws SQLException {
if (infonameId <= 0 || userId <= 0) return false;
String sql = "SELECT 1 FROM infonames WHERE id = ? AND uid = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, infonameId);
ps.setInt(2, userId);
try (ResultSet rs = ps.executeQuery()) {
return rs.next();
}
}
}
/**
* Deletes an InfoName (owner-only).
*
* @param infonameId infonames.id
* @param ownerUserId users.id
* @return true if deleted
* @throws SQLException on SQL errors
*/
public boolean deleteInfoNameOwned(int infonameId, int ownerUserId) throws SQLException {
if (infonameId <= 0) throw new IllegalArgumentException("infonameId must be > 0");
if (ownerUserId <= 0) throw new IllegalArgumentException("ownerUserId must be > 0");
String sql = "DELETE FROM infonames WHERE id = ? AND uid = ?";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, infonameId);
ps.setInt(2, ownerUserId);
return ps.executeUpdate() > 0;
}
}
// ---------- Subnames + Records ----------
/**
* Lists InfoNames owned by user (joined with TLN data).
*
* @param userId users.id
* @return rows
* @throws SQLException on SQL errors
*/
public InfoNameRow[] listOwnedInfoNames(int userId) throws SQLException {
if (userId <= 0) return new InfoNameRow[0];
String sql = """
SELECT i.id AS iid, i.info AS info, i.tln_id AS tln_id,
t.name AS tln_name, t.info AS tln_info, t.owner_id AS owner_id,
t.is_public AS is_public, t.allow_subdomains AS allow_subdomains
FROM infonames i
INNER JOIN tln t ON t.id = i.tln_id
WHERE i.uid = ?
ORDER BY t.name ASC, i.info ASC
""";
List<InfoNameRow> out = new ArrayList<>();
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
TlnRow tln = new TlnRow(
rs.getInt("tln_id"),
rs.getString("tln_name"),
rs.getString("tln_info"),
(Integer) rs.getObject("owner_id"),
rs.getInt("is_public") == 1,
rs.getInt("allow_subdomains") == 1
);
out.add(new InfoNameRow(
rs.getInt("iid"),
rs.getString("info"),
tln
));
}
}
}
return out.toArray(new InfoNameRow[0]);
}
/**
* Ensures a subname exists for an infoname; returns subnames.id.
*
* <p>If sub is null/blank, returns null (root).</p>
*
* @param infonameId infonames.id
* @param sub sub label (nullable)
* @return subnames.id or null for root
* @throws SQLException on SQL errors
*/
public Integer ensureSubname(int infonameId, String sub) throws SQLException {
if (sub == null || sub.isBlank()) return null;
if (infonameId <= 0) throw new IllegalArgumentException("infonameId must be > 0");
String name = sub.trim();
// 1) find
String find = "SELECT id FROM subnames WHERE infoname_id = ? AND name = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(find)) {
ps.setInt(1, infonameId);
ps.setString(2, name);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) return rs.getInt("id");
}
}
// 2) insert
String ins = "INSERT INTO subnames (name, infoname_id) VALUES (?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(ins, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, name);
ps.setInt(2, infonameId);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for subnames.id");
}
/**
* Adds a DNS-like record.
*
* @param infonameId infonames.id
* @param subnameId subnames.id or null for root
* @param type enum type (A/AAAA/TXT/CNAME/MX/SRV/NS)
* @param value record value
* @param ttl ttl (default 3600)
* @param priority priority (MX/SRV)
* @param port port (SRV)
* @param weight weight (SRV)
* @return new records.id
* @throws SQLException on SQL errors
*/
public int addRecord(int infonameId,
Integer subnameId,
String type,
String value,
int ttl,
Integer priority,
Integer port,
Integer weight) throws SQLException {
if (infonameId <= 0) throw new IllegalArgumentException("infonameId must be > 0");
if (type == null || type.isBlank()) throw new IllegalArgumentException("type must not be blank");
if (value == null || value.isBlank()) throw new IllegalArgumentException("value must not be blank");
if (ttl <= 0) ttl = 3600;
String t = type.trim().toUpperCase();
String sql = """
INSERT INTO records (infoname_id, subname_id, type, value, ttl, priority, port, weight)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, infonameId);
if (subnameId == null) ps.setNull(2, Types.INTEGER);
else ps.setInt(2, subnameId);
ps.setString(3, t);
ps.setString(4, value.trim());
ps.setInt(5, ttl);
if (priority == null) ps.setNull(6, Types.INTEGER);
else ps.setInt(6, priority);
if (port == null) ps.setNull(7, Types.INTEGER);
else ps.setInt(7, port);
if (weight == null) ps.setNull(8, Types.INTEGER);
else ps.setInt(8, weight);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for records.id");
}
/**
* TLN row.
*/
public record TlnRow(int id, String name, String info, Integer ownerId, boolean isPublic, boolean allowSubdomains) {
}
/**
* InfoName row (includes TLN).
*/
public record InfoNameRow(int id, String info, TlnRow tln) {
}
}

View File

@@ -0,0 +1,122 @@
package ins.frontend.utils;
import org.openautonomousconnection.webserver.utils.PasswordHasher;
import java.util.Objects;
/**
* Registration service for creating users with secure password hashing and basic validation.
*/
public final class RegistrationService {
private static final int USERNAME_MIN = 5;
private static final int USERNAME_MAX = 256;
private static final int PASSWORD_MIN = 6;
private static final int PASSWORD_MAX = 256;
private final UserDao userDao;
private final PasswordHasher hasher;
/**
* Creates a registration service.
*
* @param userDao user DAO
* @param hasher password hasher
*/
public RegistrationService(UserDao userDao, PasswordHasher hasher) {
this.userDao = Objects.requireNonNull(userDao, "userDao");
this.hasher = Objects.requireNonNull(hasher, "hasher");
}
private static String normalizeUsername(String u) {
if (u == null) return null;
String t = u.trim();
return t.isEmpty() ? null : t;
}
private static String validate(String username, String password) {
if (username == null) return "Missing username.";
if (password == null) return "Missing password.";
if (username.length() < USERNAME_MIN) return "Username too short (min " + USERNAME_MIN + ").";
if (username.length() > USERNAME_MAX) return "Username too long (max " + USERNAME_MAX + ").";
// Allow only a safe subset to avoid weird edge cases in UI and future.
for (int i = 0; i < username.length(); i++) {
char c = username.charAt(i);
boolean ok = (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_' || c == '-' || c == '.';
if (!ok) return "Username contains invalid characters.";
}
if (password.length() < PASSWORD_MIN) return "Password too short (min " + PASSWORD_MIN + ").";
if (password.length() > PASSWORD_MAX) return "Password too long.";
return null;
}
/**
* Registers a new user.
*
* @param usernameRaw raw username (from form)
* @param passwordRaw raw password (from form)
* @return result containing either userId or an error message
*/
public Result register(String usernameRaw, String passwordRaw) {
String username = normalizeUsername(usernameRaw);
String password = (passwordRaw == null) ? "" : passwordRaw;
String validationError = validate(username, password);
if (validationError != null) {
return Result.error(validationError);
}
try {
// Choose: store username as-is OR store sha256(username).
// Your schema says: username(sha256 hex or plain) -> keep it plain for now.
String usernameStored = username;
if (userDao.findByUsername(usernameStored).isPresent()) {
return Result.error("Username already exists.");
}
String passwordEncoded = hasher.hash(password);
int userId = userDao.createUserWithNewUuid(usernameStored, passwordEncoded);
return Result.ok(userId);
} catch (Exception e) {
return Result.error("Registration failed: " + e.getMessage());
}
}
/**
* Registration result.
*
* @param ok whether succeeded
* @param userId created user id, or -1
* @param error error message, or null
*/
public record Result(boolean ok, int userId, String error) {
/**
* Creates a success result.
*
* @param userId user id
* @return result
*/
public static Result ok(int userId) {
return new Result(true, userId, null);
}
/**
* Creates an error result.
*
* @param error error message
* @return result
*/
public static Result error(String error) {
return new Result(false, -1, error);
}
}
}

154
frontend/utils/UserDao.java Normal file
View File

@@ -0,0 +1,154 @@
package ins.frontend.utils;
import org.openautonomousconnection.webserver.utils.Sha256;
import java.sql.*;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
/**
* DAO for users table:
* users(id, uid(uuid string), username(sha256 hex or plain), password(pbkdf2...))
*/
public final class UserDao {
private final DataSourceProvider dataSource;
/**
* Creates a UserDao.
*
* @param dataSource connection provider
*/
public UserDao(DataSourceProvider dataSource) {
this.dataSource = Objects.requireNonNull(dataSource, "dataSource");
}
/**
* Finds a user by username.
*
* @param uid exact uid match
* @return optional row
* @throws SQLException on SQL errors
*/
public Optional<UserRow> findByUid(UUID uid) throws SQLException {
if (uid == null) return Optional.empty();
String sql = "SELECT id, uid, username, password FROM users WHERE uid = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, uid.toString());
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(new UserRow(
rs.getInt("id"),
rs.getString("uid"),
rs.getString("username"),
rs.getString("password")
));
}
}
}
/**
* Creates a new user.
*
* @param uid uuid string (36 chars)
* @param username username string (your choice: raw or sha256 hex)
* @param passwordEncoded encoded password
* @return generated users.id
* @throws SQLException on SQL errors
*/
public int createUser(String uid, String username, String passwordEncoded) throws SQLException {
if (uid == null || uid.isBlank()) throw new IllegalArgumentException("uid must not be blank");
if (username == null || username.isBlank()) throw new IllegalArgumentException("username must not be blank");
if (passwordEncoded == null || passwordEncoded.isBlank())
throw new IllegalArgumentException("passwordEncoded must not be blank");
String sql = "INSERT INTO users (uid, username, password) VALUES (?, ?, ?)";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, uid);
ps.setString(2, Sha256.hex(username));
ps.setString(3, passwordEncoded);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) return rs.getInt(1);
}
}
throw new SQLException("No generated key returned for users.id");
}
/**
* Convenience: creates a user with a new random UUID.
*
* @param username username
* @param passwordEncoded encoded password
* @return generated users.id
* @throws SQLException on SQL errors
*/
public int createUserWithNewUuid(String username, String passwordEncoded) throws SQLException {
UUID uuid = UUID.randomUUID();
while (findByUid(uuid).isPresent()) uuid = UUID.randomUUID();
return createUser(uuid.toString(), username, passwordEncoded);
}
/**
* Finds a user by username.
*
* @param username exact username match
* @return optional row
* @throws SQLException on SQL errors
*/
public Optional<UserRow> findByUsername(String username) throws SQLException {
if (username == null || username.isBlank()) return Optional.empty();
username = Sha256.hex(username);
String sql = "SELECT id, uid, username, password FROM users WHERE username = ? LIMIT 1";
try (Connection c = dataSource.getConnection();
PreparedStatement ps = c.prepareStatement(sql)) {
ps.setString(1, username);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) return Optional.empty();
return Optional.of(new UserRow(
rs.getInt("id"),
rs.getString("uid"),
rs.getString("username"),
rs.getString("password")
));
}
}
}
/**
* Connection provider abstraction.
*/
public interface DataSourceProvider {
/**
* @return open SQL connection
* @throws SQLException on errors
*/
Connection getConnection() throws SQLException;
}
/**
* User row.
*
* @param id users.id
* @param uid users.uid (uuid)
* @param username users.username
* @param passwordEncoded users.password
*/
public record UserRow(int id, String uid, String username, String passwordEncoded) {
}
}

113
frontend/utils/WebApp.java Normal file
View File

@@ -0,0 +1,113 @@
package ins.frontend.utils;
import dev.unlegitdqrk.unlegitlibrary.file.ConfigurationManager;
import org.openautonomousconnection.webserver.utils.PasswordHasher;
import org.openautonomousconnection.webserver.utils.Pbkdf2Sha256Hasher;
import java.io.File;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Objects;
/**
* Application singleton holding DAO + password hasher.
*/
public final class WebApp {
private static volatile WebApp INSTANCE;
private static boolean isInit;
private static UserDao.DataSourceProvider dsp = null;
private final UserDao userDao;
private final RegistrarDao registrarDao;
private final PasswordHasher passwordHasher;
private WebApp(UserDao userDao, RegistrarDao registrarDao, PasswordHasher passwordHasher) {
this.userDao = Objects.requireNonNull(userDao, "userDao");
this.registrarDao = Objects.requireNonNull(registrarDao, "registrarDao");
this.passwordHasher = Objects.requireNonNull(passwordHasher, "passwordHasher");
}
/**
* Initializes the singleton (call once at server startup).
*/
public static void init() {
if (isInit) return;
try {
if (!new File(new File(".").getParent(), "db.properties").exists()) {
new File(new File(".").getParent(), "db.properties").createNewFile();
}
ConfigurationManager config = new ConfigurationManager(new File(new File(".").getParent(), "db.properties"));
config.loadProperties();
if (!config.isSet("db.url")) {
config.set(
"db.url",
"jdbc:mariadb://localhost:3306/ins?useUnicode=true&characterEncoding=utf8"
);
config.saveProperties();
}
if (!config.isSet("db.user")) {
config.set("db.user", "username");
config.saveProperties();
}
if (!config.isSet("db.password")) {
config.set("db.password", "password");
config.saveProperties();
}
dsp = () -> {
try {
isInit = true;
return DriverManager.getConnection(config.getString("db.url"), config.getString("db.user"), config.getString("db.password"));
} catch (SQLException e) {
throw new SQLException("Failed to open DB connection", e);
}
};
} catch (IOException e) {
throw new RuntimeException("Failed to create config", e);
}
UserDao udao = new UserDao(dsp);
RegistrarDao rdao = new RegistrarDao(dsp);
PasswordHasher hasher = new Pbkdf2Sha256Hasher(150_000, 16, 32);
INSTANCE = new WebApp(udao, rdao, hasher);
}
/**
* Returns the initialized instance.
*
* @return app
*/
public static WebApp get() {
WebApp v = INSTANCE;
if (v == null) throw new IllegalStateException("Oac2WebApp not initialized. Call Oac2WebApp.init(...) first.");
return v;
}
/**
* @return user DAO
*/
public UserDao users() {
return userDao;
}
/**
* @return registrar DAO
*/
public RegistrarDao dao() {
return registrarDao;
}
/**
* @return password hasher
*/
public PasswordHasher passwordHasher() {
return passwordHasher;
}
}