Skip to content

Commit

Permalink
Merge pull request #20 from CyberLions/C-VPN-API
Browse files Browse the repository at this point in the history
VPN Request API
  • Loading branch information
bman46 authored Sep 30, 2022
2 parents 0c2ab18 + 7ea2742 commit 0648b97
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CCSODiscordBot/CCSODiscordBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
<None Remove="Modules\UserManagement\AccountVerification\" />
<None Remove="Modules\ServerConfig\" />
<None Remove="Modules\UserManagement\RoleSelect\" />
<None Remove="Services\VPNAPI\" />
<None Remove="Modules\VPNRequest\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.8.1" />
Expand Down Expand Up @@ -60,6 +62,8 @@
<Folder Include="Modules\UserManagement\AccountVerification\" />
<Folder Include="Modules\ServerConfig\" />
<Folder Include="Modules\UserManagement\RoleSelect\" />
<Folder Include="Services\VPNAPI\" />
<Folder Include="Modules\VPNRequest\" />
</ItemGroup>
<ItemGroup>
<None Update="Modules\Memes\Media\simpy.png">
Expand Down
2 changes: 1 addition & 1 deletion CCSODiscordBot/Modules/Roles/ComponentRespond.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public async Task ProtectedRoleButton(ulong roleId)
}
// Check for verification:
var dbUser = await _IUserRepository.GetByDiscordIdAsync(Context.User.Id, Context.Guild.Id);
if (dbUser == null || !dbUser.verified)
if (dbUser == null || !dbUser.Verified)
{
// btn
ButtonBuilder getStartedButton = new ButtonBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public async Task Verify([Summary("Code", "The code sent to your email.")][MinVa
await Context.Interaction.FollowupAsync("You have not registered with the bot yet.");
}
// Check to see if the user has already been verified:
else if (user.verified)
else if (user.Verified)
{
await Context.Interaction.FollowupAsync("Your account has already been verified.");
var guild = await _IGuildRepository.GetByDiscordIdAsync(Context.Guild.Id);
Expand All @@ -49,7 +49,7 @@ public async Task Verify([Summary("Code", "The code sent to your email.")][MinVa
// Check to see if the code matched the one sent to their email:
else if(user.VerificationNumber == code)
{
user.verified = true;
user.Verified = true;
user.VerificationNumber = null;
await _IUserRepository.UpdateUserAsync(user);
var guild = await _IGuildRepository.GetByDiscordIdAsync(Context.Guild.Id);
Expand Down
2 changes: 1 addition & 1 deletion CCSODiscordBot/Modules/UserManagement/Greeting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public async Task UserJoin(SocketGuildUser user)
var dbUser = await _iUserRepository.GetByDiscordIdAsync(user.Id, user.Guild.Id);
// Check if guild has enabled the greeting module
// Also check if the user was already added to the DB and verified:
if (!dbGuild.WelcomeEnabled || (dbUser != null && dbUser.verified))
if (!dbGuild.WelcomeEnabled || (dbUser != null && dbUser.Verified))
{
// If not, return
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public async Task WelcomeButton(ulong userID)
// (try to) Get user from DB:
var dbUser = await _iUserRepository.GetByDiscordIdAsync(Context.User.Id, Context.Guild.Id);
// Ensure user has not been welcomed before:
if (dbUser != null && dbUser.verified)
if (dbUser != null && dbUser.Verified)
{
await Context.Interaction.RespondAsync("You have already verified your membership. Please contact a mod if you are having issues or would like to update your information.", ephemeral: true);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task ModalResponse(BasicInformationEmbed modal)
if(user.Email != email.Address)
{
user.Email = email.Address;
user.verified = false;
user.Verified = false;
user.VerificationNumber = null;
}

Expand All @@ -79,7 +79,7 @@ public async Task ModalResponse(BasicInformationEmbed modal)
user.Email = email.Address;
user.FirstName = modal.FirstName.Trim();
user.LastName = modal.LastName.Trim();
user.verified = false;
user.Verified = false;
user.VerificationNumber = null;

// Add to DB:
Expand All @@ -98,7 +98,7 @@ public async Task ModalResponse(BasicInformationEmbed modal)

// See if email validation is needed:
// Only needed for PSU emails
if(!user.verified && psuEmail)
if(!user.Verified && psuEmail)
{
// Verification needed:
Random random = new Random();
Expand Down
50 changes: 50 additions & 0 deletions CCSODiscordBot/Modules/VPNRequest/AdminSlashCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using CCSODiscordBot.Services.Database.DataTables;
using CCSODiscordBot.Services.Database.Repository;
using Discord;
using Discord.Commands;
using Discord.Interactions;

namespace CCSODiscordBot.Modules.VPNRequest
{
public class AdminSlashCommands : InteractionModuleBase<ShardedInteractionContext>
{
private readonly IGuildRepository _iGuildRepository;
public AdminSlashCommands(IGuildRepository iGuildRepository)
{
_iGuildRepository = iGuildRepository;
}
[SlashCommand("setvpnrequest", "Sets the VPN request URL and API key.")]
[EnabledInDm(false)]
[DefaultMemberPermissions(GuildPermission.Administrator)]
public async Task SetVPNApiKey(string url, string key)
{
await Context.Interaction.DeferAsync(true);
Uri urlvalid;
// validate URL:
bool result = Uri.TryCreate(url, UriKind.Absolute, out urlvalid) && urlvalid != null && (urlvalid.Scheme == Uri.UriSchemeHttp || urlvalid.Scheme == Uri.UriSchemeHttps);
if (!result)
{
await Context.Interaction.FollowupAsync("The URL is invalid.", ephemeral: true);
return;
}
// Ensure guild is configured:
var guild = await _iGuildRepository.GetByDiscordIdAsync(Context.Guild.Id);
if (guild == null)
{
await Context.Interaction.FollowupAsync("The guild must be set up and verification must be enabled.", ephemeral: true);
return;
}
// Add values to guild:
guild.VPNAPIURL = urlvalid?.ToString();
guild.VPNAPIKey = key;

// Update the DB:
await _iGuildRepository.UpdateGuildAsync(guild);
// Inform user:
await Context.Interaction.FollowupAsync("Enabled VPN requests.", ephemeral: true);
Console.WriteLine("VPN Request enabled. User, Guild: " + Context.User.Id + ", " + Context.Guild.Name);
}
}
}

75 changes: 75 additions & 0 deletions CCSODiscordBot/Modules/VPNRequest/UserSlashCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using CCSODiscordBot.Services.Database.Repository;
using CCSODiscordBot.Services.VPNAPI;
using Discord;
using Discord.Interactions;

namespace CCSODiscordBot.Modules.VPNRequest
{
public class UserSlashCommands : InteractionModuleBase<ShardedInteractionContext>
{
private readonly IGuildRepository _iGuildRepository;
private readonly IUserRepository _iUserRepository;
public UserSlashCommands(IGuildRepository guildRepository, IUserRepository userRepository)
{
_iGuildRepository = guildRepository;
_iUserRepository = userRepository;
}
[SlashCommand("requestvpn", "Requests a VPN account.")]
[DefaultMemberPermissions(GuildPermission.SendMessages)]
[EnabledInDm(false)]
public async Task RequestVPNAccount([MaxLength(100)][Summary(description: "Please enter a breif reason for why you are requesting the VPN.")]string reason)
{
await Context.Interaction.DeferAsync(true);
// get and check guild for configuration:
var guild = await _iGuildRepository.GetByDiscordIdAsync(Context.Guild.Id);
if (guild == null)
{
await Context.Interaction.FollowupAsync("The guild has not been configured to support this command. Please ask an admin for further help.", ephemeral: true);
return;
}
else if(string.IsNullOrEmpty(guild.VPNAPIKey) || string.IsNullOrEmpty(guild.VPNAPIURL))
{
await Context.Interaction.FollowupAsync("The guild has not been configured to support this command. Please ask an admin for further help. Error: empty url/key", ephemeral: true);
return;
}

// Get user:
var user = await _iUserRepository.GetByDiscordIdAsync(Context.User.Id, Context.Guild.Id);
if(user is null || !user.Verified)
{
ButtonBuilder getStartedButton = new ButtonBuilder();
getStartedButton.WithLabel("Get Started");
getStartedButton.Style = ButtonStyle.Success;
getStartedButton.WithCustomId("get-started-" + Context.User.Id);
ComponentBuilder component = new ComponentBuilder();
component.WithButton(getStartedButton);

await Context.Interaction.FollowupAsync("You must have a verified PSU email registered with the bot to request VPN access.", ephemeral: true, components: component.Build());
return;
}
// Check to see if the request was already sent:
if (user.VpnRequestSent)
{
await Context.Interaction.FollowupAsync("You have already requested VPN access. Please contact an admin if you are having issues.", ephemeral: true);
return;
}
// Log event:
Console.WriteLine("Making VPN request for " + Context.User.Id + ", guild: " + Context.Guild.Name);
// Make the request and ensure it was successful:
bool requestStatus = await RequestHandler.MakeVPNRequest(guild, user, Context.User.ToString(), reason);
if (requestStatus)
{
user.VpnRequestSent = true;
// Update the DB:
await _iUserRepository.UpdateUserAsync(user);
await Context.Interaction.FollowupAsync("Your request was sent. Once your request has been accepted, you will receive an email with your login credentials. The email will be sent to "+user.Email+".", ephemeral: true);
}
else
{
await Context.Interaction.FollowupAsync("Request failed. Please try again later.", ephemeral: true);
}
}
}
}

12 changes: 12 additions & 0 deletions CCSODiscordBot/Modules/VPNRequest/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# VPN Request Module
This module makes an API request to an API that issues user credentials to log into a VPN for a lab.
## Slash Commands
- /setvpnrequest
- By default, only admins can use this command.
- This command should be provided a URL endpoint to post data to as well as an API token to authenticate to the server with.
- Set by guild
- /requestvpn
- This command can be used by anyone with send message permission (by default).
- Will send the user's information to the VPN API to create an account.
- Will only work if the user is verified and the server has the VPN request API set up.
- Use `/setvpnrequest` to set the VPN API up
21 changes: 20 additions & 1 deletion CCSODiscordBot/Services/Database/DataTables/Guild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,22 @@ namespace CCSODiscordBot.Services.Database.DataTables
{
public class Guild
{
public Guild()
{
this.Id = null;
DiscordID = default;
WelcomeEnabled = false;
VerifiedMemberRole = null;
ClassStandings = null;
LeaveEnabled = false;
LeaveChannel = null;
VPNAPIKey = null;
VPNAPIURL = null;
}

[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string? Id { get; set; }

[BsonElement("discordId")]
public ulong DiscordID { get; set; }
Expand All @@ -34,6 +47,12 @@ public class Guild

[BsonElement("leaveChannel")]
public ulong? LeaveChannel { get; set; }

[BsonElement("vpnApiKey")]
public string? VPNAPIKey { get; set; }

[BsonElement("vpnApiURL")]
public string? VPNAPIURL { get; set; }
}
}

26 changes: 21 additions & 5 deletions CCSODiscordBot/Services/Database/DataTables/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@ namespace CCSODiscordBot.Services.Database.DataTables
{
public class User
{
public User()
{
Id = null;
DiscordID = default;
DiscordGuildID = default;
FirstName = null;
LastName = null;
Email = null;
Verified = false;
VerificationNumber = null;
VpnRequestSent = false;
}

[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string? Id { get; set; }

[BsonElement("discordId")]
public ulong DiscordID { get; set; }
Expand All @@ -17,19 +30,22 @@ public class User
public ulong DiscordGuildID { get; set; }

[BsonElement("fname")]
public string FirstName { get; set; }
public string? FirstName { get; set; }

[BsonElement("lname")]
public string LastName { get; set; }
public string? LastName { get; set; }

[BsonElement("email")]
public string Email { get; set; }
public string? Email { get; set; }

[BsonElement("verified")]
public bool verified { get; set; }
public bool Verified { get; set; }

[BsonElement("verificationNumber")]
public int? VerificationNumber { get; set; }

[BsonElement("vpnRequestSent")]
public bool VpnRequestSent { get; set; }
}
}

31 changes: 31 additions & 0 deletions CCSODiscordBot/Services/VPNAPI/RequestData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Text.Json.Serialization;

namespace CCSODiscordBot.Services.VPNAPI
{
public class RequestData
{
public RequestData(string discord, string first, string last, string psuemail, string reason, string username)
{
this.DiscordName = discord;
this.FirstName = first;
this.LastName = last;
this.PSUEmail = psuemail;
this.Reason = reason;
this.DiscordUsername = username;
}
[JsonPropertyName("discord")]
public string DiscordName { get; set; }
[JsonPropertyName("firstname")]
public string FirstName { get; set; }
[JsonPropertyName("lastname")]
public string LastName { get; set; }
[JsonPropertyName("psuemail")]
public string PSUEmail { get; set; }
[JsonPropertyName("reason")]
public string Reason { get; set; }
[JsonPropertyName("discordname")]
public string DiscordUsername { get; set; }
}
}

49 changes: 49 additions & 0 deletions CCSODiscordBot/Services/VPNAPI/RequestHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Net.Http.Headers;
using System.Net.Sockets;
using System.Text.Json;
using CCSODiscordBot.Services.Database.DataTables;

namespace CCSODiscordBot.Services.VPNAPI
{
public class RequestHandler
{
/// <summary>
/// Make a request to the vpn api
/// </summary>
/// <param name="guild">The guild to use for configuration details</param>
/// <param name="user">The user to request the VPN for</param>
/// <param name="username">The discord username for the user to send the data for</param>
/// <param name="reason">The reason the user needs access to the VPN</param>
/// <returns></returns>
public static async Task<bool> MakeVPNRequest(Guild guild, User user, string username, string reason)
{
// Check for null values:
if (guild.VPNAPIKey == null || guild.VPNAPIURL == null || user.FirstName == null || user.LastName == null || user.Email == null || !user.Verified)
{
return false;
}

RequestData data = new RequestData(user.DiscordID.ToString(), user.FirstName, user.LastName, user.Email, reason, username);
using (HttpClient client = new HttpClient())
{

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("St2-Api-Key", guild.VPNAPIKey);
using (HttpContent content = new StringContent(JsonSerializer.Serialize(data), System.Text.Encoding.UTF8, "application/json"))
{
HttpResponseMessage result = await client.PostAsync(new Uri(guild.VPNAPIURL), content);

if (result.IsSuccessStatusCode)
{
Console.WriteLine("VPN Request success: " + user.DiscordID);
return true;
}
Console.WriteLine("VPN Request failed: " + user.DiscordID + " " + result.StatusCode + " " + result.ReasonPhrase);
return false;
}
}
}
}
}

0 comments on commit 0648b97

Please sign in to comment.