InstanceUserController

This commit is contained in:
Jordan Brown
2018-07-22 11:43:48 -04:00
parent 25c831a46c
commit a5e3f68cc2
8 changed files with 212 additions and 25 deletions
@@ -13,13 +13,15 @@ namespace Tgstation.Server.Api.Models
/// The <see cref="Internal.User.Id"/> of the <see cref="User"/> the <see cref="InstanceUser"/> belongs to
/// </summary>
[Permissions(DenyWrite = true)]
public long UserId { get; set; }
[Required]
public long? UserId { get; set; }
/// <summary>
/// The <see cref="Instance.Id"/> of the <see cref="Instance"/> the <see cref="InstanceUser"/> belongs to
/// </summary>
[Permissions(DenyWrite = true)]
public long InstanceId { get; set; }
[Required]
public long? InstanceId { get; set; }
/// <summary>
/// The <see cref="Rights.ByondRights"/> of the <see cref="InstanceUser"/>
@@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Rights;
@@ -63,7 +64,7 @@ namespace Tgstation.Server.Host.Controllers
DreamMakerSettings = new DreamMakerSettings(),
Name = model.Name,
Online = false,
Path = model.Path,
Path = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? model.Path.ToUpperInvariant() : model.Path,
RepositorySettings = new RepositorySettings()
};
@@ -91,15 +92,12 @@ namespace Tgstation.Server.Host.Controllers
throw;
}
}
catch (Exception e)
catch (DbUpdateConcurrencyException e)
{
return Conflict(new { message = e.Message });
}
model.Online = newInstance.Online;
model.Id = newInstance.Id;
return Json(model);
return Json(newInstance.ToApi());
}
/// <inheritdoc />
@@ -0,0 +1,161 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Host.Models;
using Tgstation.Server.Host.Security;
using Z.EntityFramework.Plus;
namespace Tgstation.Server.Host.Controllers
{
/// <summary>
/// For managing <see cref="User"/>s
/// </summary>
[Route("/" + nameof(Models.InstanceUser))]
public sealed class InstanceUserController : ModelController<Api.Models.InstanceUser>
{
/// <summary>
/// The <see cref="ILogger"/> for the <see cref="InstanceUserController"/>
/// </summary>
readonly ILogger<InstanceUserController> logger;
/// <summary>
/// Construct a <see cref="UserController"/>
/// </summary>
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the <see cref="ApiController"/></param>
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger<InstanceUserController> logger) : base(databaseContext, authenticationContextFactory)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Checks a <paramref name="model"/> for errors
/// </summary>
/// <param name="model">The <see cref="Api.Models.InstanceUser"/> to check</param>
/// <returns>A <see cref="BadRequestResult"/> explaining any errors, <see langword="null"/> if none</returns>
BadRequestObjectResult StandardModelChecks(Api.Models.InstanceUser model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
if (model.InstanceId.HasValue && model.InstanceId != Instance.Id)
return BadRequest(new { message = "InstanceId does not match headers!" });
if (!model.InstanceId.HasValue)
return BadRequest(new { message = "Missing UserId!" });
return null;
}
/// <inheritdoc />
[TgsAuthorize(AdministrationRights.EditUsers)]
public override async Task<IActionResult> Create([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
{
var test = StandardModelChecks(model);
if (test != null)
return test;
var dbUser = new Models.InstanceUser
{
ByondRights = model.ByondRights ?? ByondRights.None,
ChatSettingsRights = model.ChatSettingsRights ?? ChatSettingsRights.None,
ConfigurationRights = model.ConfigurationRights ?? ConfigurationRights.None,
DreamDaemonRights = model.DreamDaemonRights ?? DreamDaemonRights.None,
DreamMakerRights = model.DreamMakerRights ?? DreamMakerRights.None,
RepositoryRights = model.RepositoryRights ?? RepositoryRights.None,
UserId = model.UserId
};
var ourInstance = new Models.Instance
{
Id = Instance.Id
};
DatabaseContext.Instances.Attach(ourInstance);
ourInstance.InstanceUsers.Add(dbUser);
try
{
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
}
catch (DbUpdateConcurrencyException e)
{
return Conflict(new { message = e.Message });
}
return Json(dbUser.ToApi());
}
/// <inheritdoc />
[TgsAuthorize(AdministrationRights.EditUsers)]
public override async Task<IActionResult> Update([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
{
var test = StandardModelChecks(model);
if (test != null)
return test;
var originalUser = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == model.UserId).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
if (originalUser == null)
return StatusCode((int)HttpStatusCode.Gone);
originalUser.ByondRights = model.ByondRights ?? originalUser.ByondRights;
originalUser.ChatSettingsRights = model.ChatSettingsRights ?? originalUser.ChatSettingsRights;
originalUser.ConfigurationRights = model.ConfigurationRights ?? originalUser.ConfigurationRights;
originalUser.DreamDaemonRights = model.DreamDaemonRights ?? originalUser.DreamDaemonRights;
originalUser.DreamMakerRights = model.DreamMakerRights ?? originalUser.DreamMakerRights;
try
{
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
}
catch (DbUpdateConcurrencyException e)
{
return Conflict(new { message = e.Message });
}
return Json(originalUser.ToApi());
}
/// <inheritdoc />
[TgsAuthorize]
public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult(AuthenticationContext.InstanceUser != null ? (IActionResult)Json(AuthenticationContext.InstanceUser.ToApi()) : NotFound());
/// <inheritdoc />
[TgsAuthorize(InstanceUserRights.ReadUsers)]
public override async Task<IActionResult> List(CancellationToken cancellationToken)
{
var users = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).ToListAsync(cancellationToken).ConfigureAwait(false);
return Json(users.Select(x => x.ToApi()));
}
/// <inheritdoc />
[TgsAuthorize(InstanceUserRights.ReadUsers)]
public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
{
//this functions as userId
var user = await DatabaseContext.Instances.Where(x => x.Id == id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
if (user == default)
return NotFound();
return Json(user.ToApi());
}
/// <inheritdoc />
[TgsAuthorize(AdministrationRights.EditUsers)]
public override async Task<IActionResult> Delete([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
{
var test = StandardModelChecks(model);
if (test != null)
return test;
await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == model.UserId).DeleteAsync(cancellationToken).ConfigureAwait(false);
return Ok();
}
}
}
@@ -40,7 +40,7 @@ namespace Tgstation.Server.Host.Controllers
IQueryable<Job> query = DatabaseContext.Jobs;
if (Instance != null)
{
if (!AuthenticationContext.InstanceUser.AnyRights)
if (AuthenticationContext.InstanceUser?.AnyRights != true)
return Forbid();
query = query.Where(x => x.Instance.Id == Instance.Id);
}
@@ -18,32 +18,32 @@ namespace Tgstation.Server.Host.Controllers
/// For managing <see cref="User"/>s
/// </summary>
[Route("/" + nameof(Models.User))]
public sealed class UsersController : ModelController<UserUpdate>
public sealed class UserController : ModelController<UserUpdate>
{
/// <summary>
/// The <see cref="ISystemIdentityFactory"/> for the <see cref="UsersController"/>
/// The <see cref="ISystemIdentityFactory"/> for the <see cref="UserController"/>
/// </summary>
readonly ISystemIdentityFactory systemIdentityFactory;
/// <summary>
/// The <see cref="ICryptographySuite"/> for the <see cref="UsersController"/>
/// The <see cref="ICryptographySuite"/> for the <see cref="UserController"/>
/// </summary>
readonly ICryptographySuite cryptographySuite;
/// <summary>
/// The <see cref="ILogger"/> for the <see cref="UsersController"/>
/// The <see cref="ILogger"/> for the <see cref="UserController"/>
/// </summary>
readonly ILogger<UsersController> logger;
readonly ILogger<UserController> logger;
/// <summary>
/// Construct a <see cref="UsersController"/>
/// Construct a <see cref="UserController"/>
/// </summary>
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the <see cref="ApiController"/></param>
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/></param>
/// <param name="systemIdentityFactory">The value of <see cref="systemIdentityFactory"/></param>
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
public UsersController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, ILogger<UsersController> logger) : base(databaseContext, authenticationContextFactory)
public UserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, ILogger<UserController> logger) : base(databaseContext, authenticationContextFactory)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
@@ -110,9 +110,9 @@ namespace Tgstation.Server.Host.Controllers
throw;
}
}
catch (DbUpdateConcurrencyException)
catch (DbUpdateConcurrencyException e)
{
return Conflict();
return Conflict(new { message = e.Message });
}
return Json(dbUser.ToApi());
@@ -160,7 +160,7 @@ namespace Tgstation.Server.Host.Controllers
public override async Task<IActionResult> List(CancellationToken cancellationToken)
{
var users = await DatabaseContext.Users.ToListAsync(cancellationToken).ConfigureAwait(false);
return Json(users);
return Json(users.Select(x => x.ToApi()));
}
/// <inheritdoc />
@@ -170,7 +170,7 @@ namespace Tgstation.Server.Host.Controllers
var user = await DatabaseContext.Users.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
if (user == default)
return NotFound();
return Json(user);
return Json(user.ToApi());
}
}
}
@@ -106,13 +106,13 @@ namespace Tgstation.Server.Host.Models
// build default model.
LogModelBuilderHelper.Build(modelBuilder.Entity<Log>());
modelBuilder.Entity<Log>().ToTable(nameof(Logs));
modelBuilder.Entity<Instance>().HasIndex(x => x.Path).IsUnique();
modelBuilder.Entity<RevisionInformation>().HasIndex(x => x.Commit).IsUnique();
var user = modelBuilder.Entity<User>();
user.HasIndex(x => x.CanonicalName).IsUnique();
user.HasOne(x => x.CreatedBy).WithMany(x => x.CreatedUsers).OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.Instance }).IsUnique();
modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
var chatChannel = modelBuilder.Entity<ChatChannel>();
chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
+13 -1
View File
@@ -1,11 +1,12 @@
using System.Collections.Generic;
using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Models
{
/// <summary>
/// Represents an <see cref="Api.Models.Instance"/> in the database
/// </summary>
public sealed class Instance : Api.Models.Instance
public sealed class Instance : Api.Models.Instance, IApiConvertable<Api.Models.Instance>
{
/// <summary>
/// The <see cref="Models.ChatSettings"/> for the <see cref="Instance"/>
@@ -51,5 +52,16 @@ namespace Tgstation.Server.Host.Models
/// The <see cref="Jobs"/> in the <see cref="Instance"/>
/// </summary>
public List<Job> Jobs { get; set; }
/// <inheritdoc />
public Api.Models.Instance ToApi() => new Api.Models.Instance
{
AutoUpdateInterval = AutoUpdateInterval,
ConfigurationAllowed = ConfigurationAllowed,
Id = Id,
Name = Name,
Path = Path,
Online = Online
};
}
}
@@ -1,9 +1,10 @@
using System.ComponentModel.DataAnnotations;
using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Models
{
/// <inheritdoc />
public sealed class InstanceUser : Api.Models.InstanceUser
public sealed class InstanceUser : Api.Models.InstanceUser, IApiConvertable<Api.Models.InstanceUser>
{
/// <summary>
/// The row Id
@@ -24,5 +25,18 @@ namespace Tgstation.Server.Host.Models
ConfigurationRights != Api.Rights.ConfigurationRights.None ||
DreamDaemonRights != Api.Rights.DreamDaemonRights.None ||
DreamMakerRights != Api.Rights.DreamMakerRights.None;
/// <inheritdoc />
public Api.Models.InstanceUser ToApi() => new Api.Models.InstanceUser
{
ByondRights = ByondRights,
ChatSettingsRights = ChatSettingsRights,
ConfigurationRights = ConfigurationRights,
DreamDaemonRights = DreamDaemonRights,
DreamMakerRights = DreamMakerRights,
InstanceId = InstanceId,
RepositoryRights = RepositoryRights,
UserId = UserId
};
}
}