Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ It handles:

- ACL Cleanup
- Bouncer Restart
- Dev Rank Handling
- IP2ASN
- Profiler Daemon Ingest
- Profiler DB Cleanup
Expand All @@ -20,6 +21,9 @@ Removes entries from a HAproxy ACL via its socket interface in order for inactiv

Restarts our "bouncer" server every day. This is the thing that does 2FA, server queue, and region redirection.

### Dev Rank
Adds or removes members from development rank when changes are made. Performed hourly.

### IP2ASN

Caches ASNs for IP addresses of recent players.
Expand Down
6 changes: 6 additions & 0 deletions TaskDaemon.Core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
<name>TaskDaemon.Core</name>

<dependencies>
<dependency>
<groupId>me.aa07.paradise</groupId>
<artifactId>taskdaemon-database-forums</artifactId>
<version>dev-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>me.aa07.paradise</groupId>
<artifactId>taskdaemon-database-gamedb</artifactId>
Expand Down Expand Up @@ -73,6 +78,7 @@
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
Expand Down
42 changes: 30 additions & 12 deletions TaskDaemon.Core/src/me/aa07/paradise/taskdaemon/core/Core.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import me.aa07.paradise.taskdaemon.core.database.DbCore;
import me.aa07.paradise.taskdaemon.core.modules.aclcleanup.AclCleanupJob;
import me.aa07.paradise.taskdaemon.core.modules.bouncerrestart.BouncerRestartJob;
import me.aa07.paradise.taskdaemon.core.modules.devrank.DevRankJob;
import me.aa07.paradise.taskdaemon.core.modules.ip2asn.Ip2AsnJob;
import me.aa07.paradise.taskdaemon.core.modules.profilercleanup.ProfilerCleanupJob;
import me.aa07.paradise.taskdaemon.core.modules.profileringest.ProfilerWorker;
Expand Down Expand Up @@ -101,27 +102,43 @@ private void setupJobs(Scheduler scheduler, DbCore dbCore, ConfigHolder config,
jdm_bouncerrestart.put("LOGGER", logger);
jdm_bouncerrestart.put("TGS_CFG", config.tgs);
JobDetail jd_bouncerrestart = JobBuilder.newJob(BouncerRestartJob.class)
.withIdentity("bouncerrestart", "bouncerrestart")
.usingJobData(jdm_bouncerrestart)
.build();
.withIdentity("bouncerrestart", "bouncerrestart")
.usingJobData(jdm_bouncerrestart)
.build();
CronTrigger ct_bouncerrestart = TriggerBuilder.newTrigger()
.withIdentity("bouncerrestart", "bouncerrestart")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?")) // Every day - midnight
.build();
.withIdentity("bouncerrestart", "bouncerrestart")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?")) // Every day - midnight
.build();

// DevRank update
JobDataMap jdm_devrank = new JobDataMap();
jdm_devrank.put("LOGGER", logger);
jdm_devrank.put("DBCORE", dbCore);

JobDetail jd_devrank = JobBuilder.newJob(DevRankJob.class)
.withIdentity("devrank", "devrank")
.usingJobData(jdm_devrank)
.build();

CronTrigger ct_devrank = TriggerBuilder.newTrigger()
.withIdentity("devrank", "devrank")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 * * * ?")) // Every hour on the hour
.build();


// IP2ASN
JobDataMap jdm_ip2asn = new JobDataMap();
jdm_ip2asn.put("LOGGER", logger);
jdm_ip2asn.put("DBCORE", dbCore);
jdm_ip2asn.put("IP2ASNCFG", config.ip2asn);
JobDetail jd_ip2asn = JobBuilder.newJob(Ip2AsnJob.class)
.withIdentity("ip2asn", "ip2asn")
.usingJobData(jdm_ip2asn)
.build();
.withIdentity("ip2asn", "ip2asn")
.usingJobData(jdm_ip2asn)
.build();
CronTrigger ct_ip2asn = TriggerBuilder.newTrigger()
.withIdentity("ip2asn", "ip2asn")
.withSchedule(CronScheduleBuilder.cronSchedule("0 */10 * * * ?")) // Every 10 minutes
.build();
.withIdentity("ip2asn", "ip2asn")
.withSchedule(CronScheduleBuilder.cronSchedule("0 */10 * * * ?")) // Every 10 minutes
.build();

// Profiler cleanup
JobDataMap jdm_profilercleanup = new JobDataMap();
Expand All @@ -139,6 +156,7 @@ private void setupJobs(Scheduler scheduler, DbCore dbCore, ConfigHolder config,
// Schedule all
scheduler.scheduleJob(jd_aclcleanup, ct_aclcleanup);
scheduler.scheduleJob(jd_bouncerrestart, ct_bouncerrestart);
scheduler.scheduleJob(jd_devrank, ct_devrank);
scheduler.scheduleJob(jd_ip2asn, ct_ip2asn);
scheduler.scheduleJob(jd_profilercleanup, ct_profilercleanup);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.aa07.paradise.taskdaemon.core.config;

public class ConfigHolder {
public DatabaseConfig forumsDatabase;
public DatabaseConfig gameDatabase;
public Ip2AsnSerivceConfig ip2asn;
public PfsenseConfig pfsense;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package me.aa07.paradise.taskdaemon.core.config;

public class PfsenseConfig{
public class PfsenseConfig {
public String host;
public int port;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package me.aa07.paradise.taskdaemon.core.database;

public enum DatabaseType {
ProfilerDb, GameDb
ProfilerDb, GameDb, Forums
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private DataSource openDataSource(String url, String username, String password)
private void establishConnections(ConfigHolder config) {
HashMap<DatabaseType, DatabaseConfig> db_types = new HashMap<DatabaseType, DatabaseConfig>();

db_types.put(DatabaseType.Forums, config.forumsDatabase);
db_types.put(DatabaseType.GameDb, config.gameDatabase);
db_types.put(DatabaseType.ProfilerDb, config.profilerDatabase);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import me.aa07.paradise.taskdaemon.core.config.PfsenseConfig;
import me.aa07.paradise.taskdaemon.core.database.DatabaseType;
import me.aa07.paradise.taskdaemon.core.database.DbCore;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package me.aa07.paradise.taskdaemon.core.modules.devrank;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import me.aa07.paradise.taskdaemon.core.database.DatabaseType;
import me.aa07.paradise.taskdaemon.core.database.DbCore;
import me.aa07.paradise.taskdaemon.database.forums.Tables;
import me.aa07.paradise.taskdaemon.database.gamedb.tables.Admin;
import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Result;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
* Scheduled job that syncs dev team permissions between forums and game
* databases.
*/

public class DevRankJob implements Job {
private static final int DEV_TEAM_GROUP = 39;
private static final int DEV_TEAM_BITFLAG = 262144;

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap data_map = context.getMergedJobDataMap();

// Get logger and db
Logger logger = (Logger) data_map.get("LOGGER");
DbCore dbcore = (DbCore) data_map.get("DBCORE");

if (logger == null || dbcore == null) {
System.err.println("[DevRank] LOGGER or DBCORE was not set in JobDataMap.");
return;
}

logger.info("[DevRank] Starting dev rank sync");

try {
DSLContext forums_db = dbcore.jooq(DatabaseType.Forums);
DSLContext game_db = dbcore.jooq(DatabaseType.GameDb);

// Collect all dev team ckeys from forums database
List<String> dev_team_ckeys = new ArrayList<>();

Result<? extends Record> forum_records = forums_db.select(
Tables.CORE_MEMBERS.MEMBER_ID,
Tables.CORE_MEMBERS.NAME,
Tables.CORE_MEMBERS.MEMBER_GROUP_ID,
Tables.CORE_MEMBERS.MGROUP_OTHERS,
Tables.CORE_PFIELDS_CONTENT.FIELD_10).from(Tables.CORE_MEMBERS)
.leftJoin(Tables.CORE_PFIELDS_CONTENT)
.on(Tables.CORE_MEMBERS.MEMBER_ID.eq(Tables.CORE_PFIELDS_CONTENT.MEMBER_ID))
.fetch();

for (Record rec : forum_records) {
int primary_group = rec.get(Tables.CORE_MEMBERS.MEMBER_GROUP_ID);
String other_groups = rec.get(Tables.CORE_MEMBERS.MGROUP_OTHERS);
String ckey = rec.get(Tables.CORE_PFIELDS_CONTENT.FIELD_10);

Set<Integer> all_groups = new HashSet<Integer>();
all_groups.add(primary_group);

if (other_groups != null && !other_groups.isBlank()) {
Arrays.stream(other_groups.split(","))
.filter(g -> !g.isBlank())
.map(Integer::parseInt)
.forEach(all_groups::add);
}

if (all_groups.contains(DEV_TEAM_GROUP)) {
if (ckey == null || ckey.isBlank()) {
logger.warn("[DevRank] Forums user {} (ID {}) has no linked ckey",
rec.get(Tables.CORE_MEMBERS.NAME), rec.get(Tables.CORE_MEMBERS.MEMBER_ID));
continue;
}

String cleaned = ckey.toLowerCase().replaceAll("[\\s_\\-]", "");
dev_team_ckeys.add(cleaned);
}
}

// Load all ingame admins
Map<String, AdminEntry> ingame_admins = new HashMap<>();

Result<? extends Record> admin_records = game_db.select(
Admin.ADMIN.ID,
Admin.ADMIN.CKEY,
Admin.ADMIN.ADMIN_RANK,
Admin.ADMIN.FLAGS).from(Admin.ADMIN).fetch();

for (Record rec : admin_records) {
String ckey = rec.get(Admin.ADMIN.CKEY);
ingame_admins.put(ckey, new AdminEntry(
rec.get(Admin.ADMIN.ID),
rec.get(Admin.ADMIN.ADMIN_RANK),
rec.get(Admin.ADMIN.FLAGS)));
}

// Apply permissions to those who need them
for (String ckey : dev_team_ckeys) {
AdminEntry entry = ingame_admins.get(ckey);

if (entry == null) {
logger.info("[DevRank] Ckey {} not in admin table, adding new dev", ckey);
game_db.insertInto(Admin.ADMIN)
.set(Admin.ADMIN.CKEY, ckey)
.set(Admin.ADMIN.ADMIN_RANK, "Developer")
.set(Admin.ADMIN.FLAGS, DEV_TEAM_BITFLAG)
.execute();
} else {
if ("Removed".equals(entry.rank()) || entry.flags() == 0) {
logger.info("[DevRank] Resetting {} to dev team with new flag", ckey);
game_db.update(Admin.ADMIN)
.set(Admin.ADMIN.ADMIN_RANK, "Developer")
.set(Admin.ADMIN.FLAGS, DEV_TEAM_BITFLAG)
.where(Admin.ADMIN.ID.eq(entry.id()))
.execute();
} else if ((entry.flags() & DEV_TEAM_BITFLAG) == 0) {
logger.info("[DevRank] Adding dev flag to {}", ckey);
game_db.update(Admin.ADMIN)
.set(Admin.ADMIN.FLAGS, entry.flags() + DEV_TEAM_BITFLAG)
.where(Admin.ADMIN.ID.eq(entry.id()))
.execute();
} else {
logger.debug("[DevRank] {} already has dev flag", ckey);
}
}
}

// Remove devteam flag from those who no longer qualify
for (Map.Entry<String, AdminEntry> entry : ingame_admins.entrySet()) {
String ckey = entry.getKey();
AdminEntry admin = entry.getValue();

if (!dev_team_ckeys.contains(ckey)) {
if (admin.flags() == DEV_TEAM_BITFLAG) {
logger.info("[DevRank] {} only had dev flag, removing rank and flags", ckey);
game_db.update(Admin.ADMIN)
.set(Admin.ADMIN.ADMIN_RANK, "Removed")
.set(Admin.ADMIN.FLAGS, 0)
.where(Admin.ADMIN.ID.eq(admin.id()))
.execute();
} else if ((admin.flags() & DEV_TEAM_BITFLAG) != 0) {
logger.info("[DevRank] {} no longer in dev team, removing dev flag only", ckey);
game_db.update(Admin.ADMIN)
.set(Admin.ADMIN.FLAGS, admin.flags() - DEV_TEAM_BITFLAG)
.where(Admin.ADMIN.ID.eq(admin.id()))
.execute();
}
}
}

logger.info("[DevRank] Finished sync, {} devteam users processed", dev_team_ckeys.size());

} catch (Exception e) {
logger.error("[DevRank] Error during sync", e);
throw new JobExecutionException(e);
}
}

private record AdminEntry(int id, String rank, int flags) {
}
}
6 changes: 6 additions & 0 deletions config.toml.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# This is an example configuration. Please see README.MD

[forumsDatabase]
host = "172.16.0.200"
username = "myuser"
password = "mypassword"
database = "paradise_forums"

[gameDatabase]
host = "172.16.0.200"
username = "myuser"
Expand Down