Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip: SS Central - whitelist, discord link #946

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 9 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 code/modules/admin/whitelist.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ GLOBAL_LIST(whitelist)
return FALSE
. = (ckey in GLOB.whitelist)

// SS220 ADDITION - SS Central
if (!. && SScentral.initialized)
. = SScentral.is_player_whitelisted(ckey)

#undef WHITELISTFILE
10 changes: 8 additions & 2 deletions config/bandastation/bandastation_config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
#TTS_CACHE_ENABLED
#TTS_API_URL_SILERO

#WHITELIST220
## SS Central
Furrior marked this conversation as resolved.
Show resolved Hide resolved
SS_CENTRAL_URL http://127.0.0.1:8000
SS_CENTRAL_TOKEN 12345678
Furrior marked this conversation as resolved.
Show resolved Hide resolved
Furrior marked this conversation as resolved.
Show resolved Hide resolved
WHITELIST_TYPE default
## Entry from the general config.txt
#USEWHITELIST

## A minimum amount of security required on roundstart
## If there is less security than this value, a percent of roundstart threat will be pushed to midround
## Example: with value of 5, if there is 2 security members out of 5, then 3/5 of roundstart threat will be moved to midround
#ROUNDSTART_SECURITY_FOR_THREAT 5

## Webhooks
#TRANSLATE_SUGGEST_WEBHOOK_URL

#INTERVIEW_WEBHOOK_URL
Expand All @@ -32,4 +38,4 @@ ROUNDSTART_RACES vulpkanin
## If players are able to create crew transfer vote
#ALLOW_CREW_TRANSFER_VOTE
## If automatic crew transfer is enabled
#ENABLE_AUTOMATIC_CREW_TRANSFER
#ENABLE_AUTOMATIC_CREW_TRANSFER
4 changes: 2 additions & 2 deletions modular_bandastation/discord/_discord.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/datum/modpack/links_change
name = "Привязка Дискорда."
desc = "Добавление привязки Дискорда."
name = "Дискорд."
desc = "Ссылка на дискорд."
author = "KOJIT2009"
40 changes: 0 additions & 40 deletions modular_bandastation/discord/code/discord.dm
Original file line number Diff line number Diff line change
@@ -1,46 +1,6 @@
/datum/config_entry/string/discordurl
default = "https://discord.gg/SS220"

/client/New()
. = ..()
prefs.discord_id = SSdiscord.lookup_id(ckey)

/datum/preferences
var/discord_id

// IF you have linked your account, this will trigger a verify of the user
/client/verify_in_discord()
// Safety checks
if(!CONFIG_GET(flag/sql_enabled))
to_chat(src, span_warning("This feature requires the SQL backend to be running."))
return

// Why this would ever be unset, who knows
var/prefix = CONFIG_GET(string/discordbotcommandprefix)
if(!prefix)
to_chat(src, span_warning("Нет префикса для discord verification"))

if(!SSdiscord || !SSdiscord.reverify_cache)
to_chat(src, span_warning("Wait for the Discord subsystem to finish initialising"))
return
var/message = ""
// Simple sanity check to prevent a user doing this too often
var/cached_one_time_token = SSdiscord.reverify_cache[usr.ckey]
if(cached_one_time_token && cached_one_time_token != "")
message = "Вы уже сгенерировали токен <br/> [cached_one_time_token] <br/> В канале дом-бота используйте команду <br/> <span class='warning'>[prefix]привязать</span>"


else
// Will generate one if an expired one doesn't exist already, otherwise will grab existing token
var/one_time_token = SSdiscord.get_or_generate_one_time_token_for_ckey(ckey)
SSdiscord.reverify_cache[usr.ckey] = one_time_token
message = "В канале дом-бота используйте команду <br/> <span class='warning'>[prefix]привязать</span> и введите туда свой токен <br/> [one_time_token]"

//Now give them a browse window so they can't miss whatever we told them
var/datum/browser/window = new/datum/browser(usr, "discordverification", "Discord verification")
window.set_content("<span>[message]</span>")
window.open()

//Please use mob or src (not usr) in these procs. This way they can be called in the same fashion as procs.
/client/verb/discord()
set name = "discord"
Expand Down
4 changes: 4 additions & 0 deletions modular_bandastation/metaserver/_metaserver.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/datum/modpack/metaserver
name = "Metaserver"
desc = "Использование вайтлиста через БД"
author = "furior, larentoun"
5 changes: 5 additions & 0 deletions modular_bandastation/metaserver/_metaserver.dme
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "_metaserver.dm"

#include "code/discord_link.dm"
#include "code/interview.dm"
#include "code/ss_central.dm"
16 changes: 16 additions & 0 deletions modular_bandastation/metaserver/code/admin.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ADMIN_VERB(wl_ban, R_BAN, "WL Ban", "Ban a player from the whitelist.", ADMIN_CATEGORY_MAIN)
var/banned_ckey = input(user, "Please specify the cckey of the player you want to ban from the whitelist.", "WL Ban", "") as string|null
Furrior marked this conversation as resolved.
Show resolved Hide resolved
banned_ckey = ckey(ckey)
if(!input)
return
var/duration_days = input(user, "Please specify the duration of the ban in days.", "Duration", "") as num|null
if(!duration_days or duration_days < 0)
return

var/reason = input(user, "Please specify the reason for the ban.", "Reason", "") as text|null

SScentral.whitelist_ban_player(banned_ckey, admin_ckey, duration_days, reason)

log_admin("[key_name(user)] banned [banned_ckey] from whitelist for [duration_days] days for reason: [reason]")
message_admins("[key_name_admin(user)] banned [banned_ckey] from whitelist for [duration_days] days for reason: [reason]")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("WL Ban", "Ckey: [banned_ckey], Duration: [duration_days], Reason: [reason]"))
34 changes: 34 additions & 0 deletions modular_bandastation/metaserver/code/discord_link.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/datum/preferences
var/discord_id

/client/New()
. = ..()
SScentral.get_player_discord_async(src)

/client/verify_in_discord()
if(!SScentral.initialized)
to_chat(src, span_warning("Привязка Discord сейчас недоступна."))

if(SScentral.discord_links[src.ckey])
to_chat(src, span_warning("Вы уже привязали свою учетную запись Discord."))

to_chat(src, span_notice("Пытаемся получить токен для входа в Discord..."))
SScentral.verify_in_discord(src)

/datum/controller/subsystem/central/proc/verify_in_discord(client/player)
var/endpoint = "[CONFIG_GET(string/ss_central_url)]/player/token/[player.ckey]"
var/list/headers = list(
"Authorization" = "Bearer [CONFIG_GET(string/ss_central_token)]"
)
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, endpoint, "", headers, CALLBACK(SScentral, PROC_REF(verify_in_discord_callback), player))

/datum/controller/subsystem/central/proc/verify_in_discord_callback(client/player, datum/http_response/response)
if(response.errored || response.status_code != 201)
stack_trace("Failed to get discord verification token: HTTP status code " + response.status_code)
return

var/list/data = json_decode(response.body)
var/login_endpoint = "[CONFIG_GET(string/ss_central_url)]/player/login?token=[data]"

to_chat(player, span_big("Авторизуйтесь в открывшемся окне. Если окно не открывается, можете открыть сами ссылку <a href='[login_endpoint]'>[login_endpoint]</a> в браузере."))
player << link(login_endpoint)
52 changes: 52 additions & 0 deletions modular_bandastation/metaserver/code/interview.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/// Doesnt need the panic bunker to work
/mob/dead/new_player/proc/check_whitelist_or_make_interviewee()
if(!CONFIG_GET(flag/panic_bunker_interview))
return
if(SScentral.is_player_whitelisted(ckey))
client.interviewee = FALSE
return
client.interviewee = TRUE

/datum/config_entry/string/interview_webhook_url

/datum/interview/approve(client/approved_by)
. = ..()
add_owner_to_whitelist(approved_by)
send_interview_webhook(src, "[approved_by.ckey] approved:")

/datum/interview_manager/enqueue(datum/interview/to_queue)
. = ..()
send_interview_webhook(to_queue, "New interview enqueued:")

/datum/interview/deny(client/denied_by)
. = ..()
send_interview_webhook(src, "[denied_by.ckey] denied:")

/datum/interview/proc/serialize_embed()
. = list(
"fields" = list(),
"author" = list(
"name" = owner_ckey
)
)
for(var/question_id in 1 to length(questions))
var/list/question_data = list(
"name" = "[questions[question_id]]",
"value" = "[isnull(responses[question_id]) ? "N/A" : responses[question_id]]"
)
.["fields"] += list(question_data)
return .

/proc/send_interview_webhook(datum/interview/interview, additional_msg)
var/webhook = CONFIG_GET(string/interview_webhook_url)
if(!webhook || !interview)
return
var/list/webhook_info = list()
webhook_info["content"] = additional_msg
webhook_info["embeds"] = list(interview.serialize_embed())
var/list/headers = list()
headers["Content-Type"] = "application/json"
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, webhook, json_encode(webhook_info), headers)

/datum/interview/proc/add_owner_to_whitelist(client/added_by)
SScentral.add_to_whitelist(owner_ckey, added_by.ckey, 365)
124 changes: 124 additions & 0 deletions modular_bandastation/metaserver/code/ss_central.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/datum/config_entry/string/ss_central_url
default = ""
protection = CONFIG_ENTRY_LOCKED

/datum/config_entry/string/ss_central_token
default = ""
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN

/datum/config_entry/string/whitelist_type
default = "default"

SUBSYSTEM_DEF(central)
var/list/discord_links = list()
flags = SS_NO_FIRE

/datum/controller/subsystem/central/Initialize()
if(!(CONFIG_GET(string/ss_central_url) && CONFIG_GET(string/ss_central_token)))
return SS_INIT_FAILURE
load_whitelist()

/datum/controller/subsystem/central/proc/load_whitelist()
var/endpoint = "[CONFIG_GET(string/ss_central_url)]/whitelist/simple/active_whitelists/ckey?wl_type=[CONFIG_GET(string/whitelist_type)]"

SShttp.create_async_request(RUSTG_HTTP_METHOD_GET, endpoint, "", list(), CALLBACK(src, PROC_REF(load_whitelist_callback)))

/datum/controller/subsystem/central/proc/load_whitelist_callback(datum/http_response/response)
if(response.errored || response.status_code != 200)
stack_trace("Failed to load whitelist: HTTP status code [response.status_code]")
return

var/list/ckeys = json_decode(response.body)
GLOB.whitelist = ckeys

/datum/controller/subsystem/central/proc/get_player_discord_async(client/player)
var/endpoint = "[CONFIG_GET(string/ss_central_url)]/player/ckey/[player.ckey]"

SShttp.create_async_request(RUSTG_HTTP_METHOD_GET, endpoint, "", list(), CALLBACK(src, PROC_REF(get_player_discord_callback), player))

/datum/controller/subsystem/central/proc/get_player_discord_callback(client/player, datum/http_response/response)
if(response.errored || response.status_code != 200)
stack_trace("Failed to get player discord: HTTP status code [response.status_code]")
return

var/list/data = json_decode(response.body)
var/discord_id = data["discord_id"]
var/ckey = data["ckey"]
discord_links[ckey] = discord_id

player.prefs.discord_id = discord_id

/datum/controller/subsystem/central/proc/is_player_discord_linked(client/player)
if(!player)
return FALSE

if(player.prefs.discord_id)
return TRUE

// If player somehow losed its id. Not sure if needed
if(SScentral.discord_links[player.ckey])
player.prefs.discord_id = SScentral.discord_links[player.ckey]
return TRUE

// Update the info just in case
SScentral.get_player_discord_async(src)

return FALSE

/// WARNING: only semi async - UNTIL based
/datum/controller/subsystem/central/proc/is_player_whitelisted(ckey)
. = (ckey in GLOB.whitelist)

var/endpoint = "[CONFIG_GET(string/ss_central_url)]/whitelist/simple/is_whitelisted/ckey/[ckey]?wl_type=[CONFIG_GET(string/whitelist_type)]"
var/datum/http_response/response = SShttp.make_blocking_request(RUSTG_HTTP_METHOD_GET, endpoint, "", list())
if(response.errored || response.status_code != 200)
return FALSE

return json_decode(response.body)

/datum/controller/subsystem/central/proc/add_to_whitelist(ckey, added_by, duration_days = 0)
var/endpoint = "[CONFIG_GET(string/ss_central_url)]/whitelist/ckey"

var/list/headers = list()
headers["Authorization"] = "Bearer [CONFIG_GET(string/ss_central_token)]"
var/list/body = list()
body["player_ckey"] = ckey
body["admin_ckey"] = added_by
body["wl_type"] = CONFIG_GET(string/whitelist_type)
body["duration_days"] = duration_days

SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, endpoint, json_encode(body), headers, CALLBACK(src, PROC_REF(add_to_whitelist_callback)))

/datum/controller/subsystem/central/proc/add_to_whitelist_callback(datum/http_response/response)
if(response.errored || response.status_code != 200)
stack_trace("Failed to add to whitelist: HTTP status code [response.status_code]")
return

var/list/data = json_decode(response.body)
var/ckey = data["ckey"]

GLOB.whitelist |= ckey

/datum/controller/subsystem/central/proc/whitelist_ban_player(player_ckey, admin_ckey, duration_days, reason)
var/endpoint = "[CONFIG_GET(string/ss_central_url)]/whitelist/ban/ckey"

var/list/headers = list()
headers["Authorization"] = "Bearer [CONFIG_GET(string/ss_central_token)]"
var/list/body = list()
body["player_ckey"] = player_ckey
body["admin_ckey"] = admin_ckey
body["wl_type"] = CONFIG_GET(string/whitelist_type)
body["duration_days"] = duration_days
body["reason"] = reason

SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, endpoint, json_encode(body), headers, CALLBACK(src, PROC_REF(whitelist_ban_player_callback)))

/datum/controller/subsystem/central/proc/whitelist_ban_player_callback(datum/http_response/response)
if(response.errored || response.status_code != 200)
stack_trace("Failed to ban player: HTTP status code [response.status_code]")
return

var/list/data = json_decode(response.body)
var/ckey = data["ckey"]

GLOB.whitelist -= ckey
2 changes: 1 addition & 1 deletion modular_bandastation/modular_bandastation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#include "species/_species.dme"
#include "translations/_translations.dme"
#include "tts/_tts.dme"
#include "whitelist220/_whitelist220.dme"
#include "metaserver/_metaserver.dme"
#include "world_topics/_world_topics.dme"
#include "preferences/_preferences.dme"
#include "jukebox/_jukebox.dme"
Expand Down
Loading
Loading