Remotely/Server/Pages/ManageOrganization.razor.cs
Jared Goodwin 3f8d63c2c0
Azure Pipelines, Docker, and embedded server data. (#543)
* Add Azure Pipelines yml.

* Remove Server Installer.

* Update Release Build.yml for Azure Pipelines

* Update Release Build.yml

* Update Release Build.yml for Azure Pipelines

* Update Release Build.yml for Azure Pipelines

* Update Release Build.yml for Azure Pipelines

* Update Release Build.yml for Azure Pipelines

* Update signtool.exe

* Update Release Build.yml

* Update Publish.ps1

* Update pipeline and Dockerfile.

* Move docker files.

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Update Dockerfile

* Move files.

* Update Dockerfile

* Update Dockerfile

* Create RewritableStream.

* Finish implementation of rewritable stream and embedded data searcher.

* Remove RelayCode.

* Get branding from default org if orgId is missing.

* Update README.md

* Remove AppConstants.ServerUrl.  Fix main module file path.

* Update submodule.

* Extract embedded data in Program.cs.

* Add logging.  Update submodule.

* Remove size block.  BinaryWriter prefixes size.

* Remove unused async

* Update Immense.RemoteControl

* Update Release Build.yml for Azure Pipelines

* Update Release Build.yml for Azure Pipelines

* Use UpgradeService for determining out of date clients.
2022-12-31 19:10:40 -08:00

372 lines
13 KiB
C#

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Remotely.Server.Components;
using Remotely.Server.Components.ModalContents;
using Remotely.Server.Services;
using Remotely.Shared.Models;
using Remotely.Shared.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace Remotely.Server.Pages
{
public partial class ManageOrganization : AuthComponentBase
{
private readonly List<DeviceGroup> _deviceGroups = new();
private readonly List<InviteLink> _invites = new();
private readonly List<RemotelyUser> _orgUsers = new();
private bool _inviteAsAdmin;
private string _inviteEmail;
private string _newDeviceGroupName;
private Organization _organization;
private string _selectedDeviceGroupId;
[Inject]
private IDataService DataService { get; set; }
[Inject]
private IEmailSenderEx EmailSender { get; set; }
[Inject]
private IJsInterop JsInterop { get; set; }
[Inject]
private IModalService ModalService { get; set; }
[Inject]
private NavigationManager NavManager { get; set; }
[Inject]
private IToastService ToastService { get; set; }
[Inject]
private UserManager<RemotelyUser> UserManager { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await RefreshData();
}
private void CreateNewDeviceGroup()
{
if (!User.IsAdministrator)
{
return;
}
if (string.IsNullOrWhiteSpace(_newDeviceGroupName))
{
return;
}
var deviceGroup = new DeviceGroup()
{
Name = _newDeviceGroupName
};
var result = DataService.AddDeviceGroup(User.OrganizationID, deviceGroup, out _, out var errorMessage);
if (!result)
{
ToastService.ShowToast(errorMessage, classString: "bg-danger");
return;
}
ToastService.ShowToast("Device group created.");
_deviceGroups.Add(deviceGroup);
_newDeviceGroupName = string.Empty;
}
private void DefaultOrgCheckChanged(ChangeEventArgs args)
{
if (!User.IsServerAdmin)
{
return;
}
var isDefault = (bool)args.Value;
DataService.SetIsDefaultOrganization(_organization.ID, isDefault);
ToastService.ShowToast("Default organization set.");
}
private async Task DeleteInvite(InviteLink invite)
{
if (!User.IsAdministrator)
{
return;
}
var result = await JsInterop.Confirm("Are you sure you want to delete this invitation?");
if (!result)
{
return;
}
DataService.DeleteInvite(User.OrganizationID, invite.ID);
_invites.RemoveAll(x => x.ID == invite.ID);
ToastService.ShowToast("Invitation deleted.");
}
private async Task DeleteSelectedDeviceGroup()
{
if (!User.IsAdministrator)
{
return;
}
if (string.IsNullOrWhiteSpace(_selectedDeviceGroupId))
{
return;
}
var result = await JsInterop.Confirm("Are you sure you want to delete this device group?");
if (!result)
{
return;
}
DataService.DeleteDeviceGroup(User.OrganizationID, _selectedDeviceGroupId);
_deviceGroups.RemoveAll(x => x.ID == _selectedDeviceGroupId);
_selectedDeviceGroupId = string.Empty;
}
private async Task DeleteUser(RemotelyUser user)
{
if (!User.IsAdministrator)
{
return;
}
if (User.Id == user.Id)
{
ToastService.ShowToast("You can't delete yourself.", classString: "bg-warning");
return;
}
var result = await JsInterop.Confirm("Are you sure you want to delete this user?");
if (!result)
{
return;
}
await DataService.DeleteUser(User.OrganizationID, user.Id);
_orgUsers.RemoveAll(x => x.Id == user.Id);
ToastService.ShowToast("User deleted.");
}
private async Task EditDeviceGroups(RemotelyUser user)
{
void editDeviceGroupsModal(RenderTreeBuilder builder)
{
var deviceGroups = DataService.GetDeviceGroupsForOrganization(user.OrganizationID);
builder.OpenComponent<EditDeviceGroup>(0);
builder.AddAttribute(1, EditDeviceGroup.EditUserPropName, user);
builder.AddAttribute(2, EditDeviceGroup.DeviceGroupsPropName, deviceGroups);
builder.CloseComponent();
}
await ModalService.ShowModal("Device Groups", editDeviceGroupsModal);
}
private async Task EvaluateInviteInputKeypress(KeyboardEventArgs args)
{
if (args.Key.Equals("Enter", StringComparison.OrdinalIgnoreCase))
{
await SendInvite();
}
}
private void EvaluateNewDeviceGroupKeyPress(KeyboardEventArgs args)
{
if (args.Key.Equals("Enter", StringComparison.OrdinalIgnoreCase))
{
CreateNewDeviceGroup();
}
}
private void OrganizationNameChanged(ChangeEventArgs args)
{
if (!User.IsAdministrator)
{
return;
}
var newName = (string)args.Value;
if (string.IsNullOrWhiteSpace(newName))
{
return;
}
if (newName.Length > 25)
{
ToastService.ShowToast("Must be 25 characters or less.",
classString: "bg-warning");
return;
}
DataService.UpdateOrganizationName(_organization.ID, newName);
_organization.OrganizationName = newName;
ToastService.ShowToast("Organization name changed.");
}
private async Task RefreshData()
{
_organization = await DataService.GetOrganizationByUserName(Username);
_orgUsers.Clear();
_invites.Clear();
_deviceGroups.Clear();
_invites.AddRange(DataService.GetAllInviteLinks(User.OrganizationID).OrderBy(x => x.InvitedUser));
_deviceGroups.AddRange(DataService.GetDeviceGroups(Username).OrderBy(x => x.Name));
var orgUsers = await DataService.GetAllUsersInOrganization(User.OrganizationID);
_orgUsers.AddRange(orgUsers.OrderBy(x => x.UserName));
}
private async Task ResetPassword(RemotelyUser user)
{
if (!User.IsAdministrator)
{
return;
}
var code = await UserManager.GeneratePasswordResetTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var resetUrl = $"{NavManager.BaseUri}Identity/Account/ResetPassword?code={code}";
await ModalService.ShowModal("Password Reset", builder =>
{
builder.AddMarkupContent(0, $@"<div class=""mb-3"">Password Reset URL:</div>
<input readonly value=""{resetUrl}"" class=""form-control"" />
<div class=""mt-3"">NOTE: Give this URL to the user. They must be logged out completely for it to work.</div>");
});
}
private async Task SendInvite()
{
if (!User.IsAdministrator)
{
return;
}
if (!DataService.DoesUserExist(_inviteEmail))
{
var result = await DataService.CreateUser(_inviteEmail, _inviteAsAdmin, User.OrganizationID);
if (result)
{
var user = await DataService.GetUserAsync(_inviteEmail);
await UserManager.ConfirmEmailAsync(user, await UserManager.GenerateEmailConfirmationTokenAsync(user));
_orgUsers.Add(user);
_inviteAsAdmin = false;
_inviteEmail = string.Empty;
ToastService.ShowToast("User account created.");
return;
}
else
{
ToastService.ShowToast("Create user failed.", classString: "bg-danger");
return;
}
}
else
{
var invite = new InviteViewModel()
{
InvitedUser = _inviteEmail,
IsAdmin = _inviteAsAdmin
};
var newInvite = DataService.AddInvite(User.OrganizationID, invite);
var inviteURL = $"{NavManager.BaseUri}Invite?id={newInvite.ID}";
var emailResult = await EmailSender.SendEmailAsync(invite.InvitedUser, "Invitation to Organization in Remotely",
$@"<img src='{NavManager.BaseUri}images/Remotely_Logo.png'/>
<br><br>
Hello!
<br><br>
You've been invited to join an organization in Remotely.
<br><br>
You can join the organization by <a href='{HtmlEncoder.Default.Encode(inviteURL)}'>clicking here</a>.",
User.OrganizationID);
if (emailResult)
{
ToastService.ShowToast("Invitation sent.");
_inviteAsAdmin = false;
_inviteEmail = string.Empty;
_invites.Add(newInvite);
}
else
{
ToastService.ShowToast("Error sending invititation email.", classString: "bg-danger");
}
}
}
private void SetUserIsAdmin(ChangeEventArgs args, RemotelyUser orgUser)
{
if (!User.IsAdministrator)
{
return;
}
var isAdmin = (bool)args.Value;
DataService.ChangeUserIsAdmin(User.OrganizationID, orgUser.Id, isAdmin);
ToastService.ShowToast("Administrator value set.");
}
private void ShowDefaultOrgHelp()
{
ModalService.ShowModal("Default Organization", new[]
{
@"This option is only available for server administrators. When selected,
it sets this organization as the default for the server. If the organization can't
be determined in the quick support apps, they will use the default organization's branding."
});
}
private void ShowDeviceGroupHelp()
{
ModalService.ShowModal("Device Groups", new[]
{
"Device groups can be used to restrict user permissions and to filter computers on " +
"the main page.",
"Everyone will have access to devices that are not in a group. Only " +
"administrators and users in a device group will have access to devices in that group."
});
}
private void ShowInvitesHelp()
{
ModalService.ShowModal("Invitations", new[]
{
"All pending invitations will be shown here and can be revoked by deleting them.",
"If a user does not exist, sending an invite will create their account and add them to the current organization. " +
"A password reset URL can be generated from the user table.",
"The Admin checkbox determines if the new user will have administrator privileges in this organization."
});
}
private void ShowUsersHelp()
{
ModalService.ShowModal("Users", new[]
{
"All users for the organization are managed here",
"Administrators will have access to this management screen as well as all computers."
});
}
}
}