More attribute stuff

This commit is contained in:
Cyberboss
2018-04-04 14:16:56 -04:00
parent d20e6d663a
commit 4e3cb4b186
13 changed files with 225 additions and 131 deletions
+20 -32
View File
@@ -9,43 +9,40 @@ namespace Tgstation.Server.Api
public sealed class ModelAttribute : Attribute
{
/// <summary>
/// Right required to read the field
/// Right required to read the model
/// </summary>
public object ReadRight { get; set; }
/// <summary>
/// Right required to write the field with an Update call
/// Right required to update the model
/// </summary>
public object WriteRight
{
get => writeRight;
set
{
if (DenyWrite)
throw new InvalidOperationException("Cannot set WriteRight on a PermissionsAttribute with DenyRight set");
writeRight = value;
}
}
public object WriteRight { get; set; }
/// <summary>
/// If the field cannot be written to
/// If the Create and Delete actions are available for this model
/// </summary>
public bool DenyWrite
{
get => denyWrite;
set
{
if (WriteRight != null)
throw new InvalidOperationException("Cannot set DenyRight on a PermissionsAttribute with WriteRight set");
denyWrite = value;
}
}
public bool CanCrud { get; set; }
/// <summary>
/// If the List action is available for this model
/// </summary>
public bool CanList { get; set; }
/// <summary>
/// If the actions require an <see cref="Models.Instance.Id"/>
/// </summary>
public bool RequiresInstance { get; set; }
/// <summary>
/// The enum type that designates access to the model. Must be an <see cref="Enum"/> with the <see cref="FlagsAttribute"/>
/// </summary>
public Type RightsEnum { get; }
/// <summary>
/// Construct a <see cref="ModelAttribute"/>
/// </summary>
public ModelAttribute() => CanList = true;
/// <summary>
/// Construct a <see cref="ModelAttribute"/>
/// </summary>
@@ -60,14 +57,5 @@ namespace Tgstation.Server.Api
throw new ArgumentException("rightsEnum must be an integer type!", nameof(rightsEnum));
RightsEnum = rightsEnum;
}
/// <summary>
/// Backing field for <see cref="WriteRight"/>
/// </summary>
object writeRight;
/// <summary>
/// Backing field for <see cref="DenyWrite"/>
/// </summary>
bool denyWrite;
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ namespace Tgstation.Server.Api.Models
/// <summary>
/// The <see cref="System.Version"/> of the <see cref="Byond"/> installation. Will be <see langword="null"/> if the user does not have permission to view it or there is no BYOND version installed. Only considers the <see cref="Version.Major"/> and <see cref="Version.Minor"/> numbers
/// </summary>
[Permissions(ComplexWrite = true, ReadRight = ByondRights.ReadInstalled)]
[Permissions(ReadRight = ByondRights.ReadInstalled, WriteRight = ByondRights.ChangeVersion)]
public Version Version { get; set; }
/// <summary>
@@ -5,9 +5,9 @@ using Tgstation.Server.Api.Rights;
namespace Tgstation.Server.Api.Models
{
/// <summary>
/// Represents an instance of BYOND's DreamDaemon game server
/// Represents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server
/// </summary>
[Model(typeof(DreamDaemonRights))]
[Model(typeof(DreamDaemonRights), CanCrud = true)]
public sealed class DreamDaemon
{
/// <summary>
@@ -4,14 +4,14 @@ using Tgstation.Server.Api.Rights;
namespace Tgstation.Server.Api.Models
{
/// <summary>
/// Represents the state of the DreamMaker compiler
/// Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
/// </summary>
[Model(typeof(DreamMakerRights), ReadRight = DreamMakerRights.Read)]
[Model(typeof(DreamMakerRights), ReadRight = DreamMakerRights.Read, CanCrud = true)]
public sealed class DreamMaker
{
/// <summary>
/// When the compilation started
/// </summary>
{
/// <summary>
/// When the compilation started
/// </summary>
[Permissions(DenyWrite = true)]
public DateTimeOffset StartedAt { get; set; }
/// <summary>
+1 -1
View File
@@ -5,7 +5,7 @@ namespace Tgstation.Server.Api.Models
/// <summary>
/// Metadata about a server instance
/// </summary>
[Model(typeof(InstanceManagerRights))]
[Model(typeof(InstanceManagerRights), CanCrud = true, CanList = true)]
public sealed class Instance
{
/// <summary>
@@ -19,14 +19,14 @@ namespace Tgstation.Server.Api.Models
/// </summary>
public ByondRights ByondRights { get; set; }
/// <summary>
/// The <see cref="DreamDaemonRights"/> of the <see cref="InstanceUser"/>
/// </summary>
DreamDaemonRights DreamDaemonRights { get; set; }
/// <summary>
/// The <see cref="DreamDaemonRights"/> of the <see cref="InstanceUser"/>
/// </summary>
public DreamDaemonRights DreamDaemonRights { get; set; }
/// <summary>
/// The <see cref="RepositoryRights"/> of the <see cref="InstanceUser"/>
/// </summary>
RepositoryRights RepositoryRights { get; set; }
/// <summary>
/// The <see cref="RepositoryRights"/> of the <see cref="InstanceUser"/>
/// </summary>
public RepositoryRights RepositoryRights { get; set; }
}
}
+41
View File
@@ -0,0 +1,41 @@
using System;
namespace Tgstation.Server.Api.Models
{
/// <summary>
/// Represents a long running job on the server
/// </summary>
[Model]
public sealed class Job
{
/// <summary>
/// The <see cref="Job"/> ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// Human readable description of the <see cref="Job"/>
/// </summary>
public string Description { get; set; }
/// <summary>
/// When the <see cref="Job"/> was started
/// </summary>
public DateTimeOffset StartedAt { get; set; }
/// <summary>
/// When the <see cref="Job"/> stopped
/// </summary>
public DateTimeOffset StoppedAt { get; set; }
/// <summary>
/// If the <see cref="Job"/> was cancelled
/// </summary>
public bool Cancelled { get; set; }
/// <summary>
/// If the <see cref="Job"/> has incremental progress, this will range from 1 - 100. 0 otherwise
/// </summary>
public int Progress { get; set; }
}
}
+9 -4
View File
@@ -1,12 +1,12 @@
using System.Net;
using Tgstation.Server.Api.Rights;
using System;
using System.Net;
namespace Tgstation.Server.Api.Models
{
/// <summary>
/// Represents an access token for the server
/// Represents an access token for the server. Read action generates a new one. Update action expires all for user
/// </summary>
[Model(typeof(TokenRights), DenyWrite = true)]
[Model]
public sealed class Token
{
/// <summary>
@@ -24,6 +24,11 @@ namespace Tgstation.Server.Api.Models
/// </summary>
public IPAddress IssuedTo { get; set; }
/// <summary>
/// When the <see cref="Token"/> was originally issued
/// </summary>
public DateTimeOffset IssuedAt { get; set; }
/// <summary>
/// The token <see cref="string"/>. Not modifiable, only appears once
/// </summary>
@@ -14,64 +14,18 @@ namespace Tgstation.Server.Api
public object ReadRight { get; set; }
/// <summary>
/// Right required to write the field with an Update call
/// Right required to update the field
/// </summary>
public object WriteRight
{
get => writeRight;
set
{
if (DenyWrite)
throw new InvalidOperationException("Cannot set WriteRight on a PermissionsAttribute with DenyRight set");
if (ComplexWrite)
throw new InvalidOperationException("Cannot set WriteRight on a PermissionsAttribute with ComplexWrite set");
writeRight = value;
}
}
public object WriteRight { get; set; }
/// <summary>
/// If the field cannot be written to
/// </summary>
public bool DenyWrite
{
get => denyWrite;
set
{
if (WriteRight != null)
throw new InvalidOperationException("Cannot set DenyRight on a PermissionsAttribute with WriteRight set");
if (ComplexWrite)
throw new InvalidOperationException("Cannot set DenyRight on a PermissionsAttribute with ComplexWrite set");
denyWrite = value;
}
}
public bool DenyWrite { get; set; }
/// <summary>
/// If the field has multiple write permissions
/// </summary>
public bool ComplexWrite
{
get => complexWrite;
set
{
if (WriteRight != null)
throw new InvalidOperationException("Cannot set ComplexWrite on a PermissionsAttribute with WriteRight set");
if (ComplexWrite)
throw new InvalidOperationException("Cannot set ComplexWrite on a PermissionsAttribute with DenyWrite set");
complexWrite = value;
}
}
/// <summary>
/// Backing field for <see cref="WriteRight"/>
/// </summary>
object writeRight;
/// <summary>
/// Backing field for <see cref="DenyWrite"/>
/// </summary>
bool denyWrite;
/// <summary>
/// Backing field for <see cref="ComplexWrite"/>
/// </summary>
bool complexWrite;
public bool ComplexWrite { get; set; }
}
}
+4 -12
View File
@@ -21,24 +21,16 @@ namespace Tgstation.Server.Api.Rights
/// </summary>
ReadStaged = 2,
/// <summary>
/// User may install any BYOND version if none is installed
/// User may change to any BYOND version
/// </summary>
Install = 4,
/// <summary>
/// User may upgrade the installed BYOND version
/// </summary>
Upgrade = 8,
/// <summary>
/// User may downgrade the installed BYOND version
/// </summary>
Downgrade = 16,
ChangeVersion = 4,
/// <summary>
/// User may cancel a pending installation job
/// </summary>
Cancel = 32,
Cancel = 8,
/// <summary>
/// User may read the <see cref="Models.ByondStatus"/> of the installation job
/// </summary>
ReadStatus = 64,
ReadStatus = 16,
}
}
@@ -15,50 +15,46 @@ namespace Tgstation.Server.Api.Rights
/// <summary>
/// User may create the <see cref="Models.Repository"/> if it does not exist
/// </summary>
Create = 1,
/// <summary>
/// User may delete the <see cref="Models.Repository"/>
/// </summary>
Delete = 2,
SetOrigin = 2,
/// <summary>
/// User may directly set the sha the <see cref="Models.Repository"/>'s HEAD points to
/// </summary>
SetSha = 8,
SetSha = 4,
/// <summary>
/// User may fetch and merge GitHub pull requests
/// </summary>
MergePullRequest = 16,
MergePullRequest = 8,
/// <summary>
/// User may use the update feature
/// </summary>
Update = 32,
UpdateBranch = 16,
/// <summary>
/// User may change <see cref="Models.Repository.CommitterName"/> and <see cref="Models.Repository.CommitterEmail"/>
/// </summary>
ChangeCommitter = 64,
ChangeCommitter = 32,
/// <summary>
/// User may change <see cref="Models.Repository.PushTestMergeCommits"/>
/// </summary>
ChangeTestMergeCommits = 128,
ChangeTestMergeCommits = 64,
/// <summary>
/// User may change <see cref="Models.Repository.AutoUpdateInterval"/>
/// </summary>
ChangeAutoUpdate = 256,
ChangeAutoUpdate = 128,
/// <summary>
/// User may read and change <see cref="Models.Repository.AccessUser"/> and <see cref="Models.Repository.AccessToken"/>
/// </summary>
ChangeCredentials = 512,
ChangeCredentials = 256,
/// <summary>
/// User may reset the <see cref="Models.Repository"/>'s HEAD to the origin reference of whatever branch it is tracking
/// </summary>
Reset = 1024,
Reset = 512,
/// <summary>
/// User may read all fields in the <see cref="Models.Repository"/> with the exception of <see cref="Models.Repository.AccessToken"/>
/// </summary>
Read = 2048,
Read = 1024,
/// <summary>
/// User may set <see cref="Models.Repository.Reference"/> to a backup tag
/// </summary>
CheckoutBackup,
CheckoutBackup = 2048,
}
}
+20
View File
@@ -0,0 +1,20 @@
using System.Net.Http;
namespace Tgstation.Server.Api
{
/// <summary>
/// Represents a route to a server action
/// </summary>
public sealed class Route
{
/// <summary>
/// The path to the action
/// </summary>
public string Path { get; set; }
/// <summary>
/// The method of the action
/// </summary>
public HttpMethod Method { get; set; }
}
}
+98
View File
@@ -0,0 +1,98 @@
using System;
using System.Linq;
using System.Net.Http;
using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Api
{
/// <summary>
/// Gets routes for a given model
/// </summary>
public static class RouteHelper
{
/// <summary>
/// Read a <typeparamref name="TModel"/>
/// </summary>
/// <typeparam name="TModel">The model type to read</typeparam>
/// <param name="objectId">The optional ID to pass in</param>
/// <returns>A route to the read action</returns>
static Route Read<TModel>(long? objectId) where TModel : class
{
var result = new Route { Path = String.Concat('/', typeof(TModel).Name), Method = HttpMethod.Get };
if (objectId.HasValue)
result.Path = String.Concat(result.Path, '/', objectId);
return result;
}
/// <summary>
/// Get the <see cref="Route"/> to the read action for a given <typeparamref name="TModel"/>
/// </summary>
/// <typeparam name="TModel">The model to read</typeparam>
/// <param name="instance"><see cref="Instance"/> to read from if required</param>
/// <returns>A <see cref="Route"/> to the read action</returns>
public static Route Read<TModel>(Instance instance) where TModel : class
{
var model = (ModelAttribute)typeof(TModel).GetCustomAttributes(typeof(ModelAttribute), false).FirstOrDefault();
if (model == default(ModelAttribute))
throw new InvalidOperationException("TModel must have the ModelAttribute");
if (model.RequiresInstance ^ instance != null)
throw (model.RequiresInstance ? new ArgumentNullException(nameof(instance)) : new ArgumentException("Instance is not used for this route!", nameof(instance)));
return Read<TModel>(instance?.Id);
}
/// <summary>
/// Get the <see cref="Route"/> to the update action for a given <typeparamref name="TModel"/>
/// </summary>
/// <typeparam name="TModel">The model to update</typeparam>
/// <returns>A <see cref="Route"/> to the update action</returns>
public static Route Update<TModel>(Instance instance) where TModel : class
{
var result = Read<TModel>(instance);
result.Method = HttpMethod.Post;
return result;
}
/// <summary>
/// Get the <see cref="Route"/> to the list action for a given <typeparamref name="TModel"/>
/// </summary>
/// <typeparam name="TModel">The model to list</typeparam>
/// <returns>A <see cref="Route"/> to the list action</returns>
public static Route List<TModel>(Instance instance) where TModel : class
{
var result = Read<TModel>(instance);
result.Path = String.Concat(result.Path, '/', nameof(List));
return result;
}
/// <summary>
/// Get the <see cref="Route"/> to the create action for a given <typeparamref name="TModel"/>
/// </summary>
/// <typeparam name="TModel">The model to create</typeparam>
/// <returns>A <see cref="Route"/> to the create action</returns>
public static Route Create<TModel>(Instance instance) where TModel : class
{
var result = Read<TModel>(instance);
result.Method = HttpMethod.Put;
return result;
}
/// <summary>
/// Get the <see cref="Route"/> to the delete action for a given <typeparamref name="TModel"/>
/// </summary>
/// <typeparam name="TModel">The model to delete</typeparam>
/// <returns>A <see cref="Route"/> to the delete action</returns>
public static Route Delete<TModel>(Instance instance) where TModel : class
{
var result = Read<TModel>(instance);
result.Method = HttpMethod.Delete;
return result;
}
/// <summary>
/// Get the <see cref="Route"/> to a given <paramref name="job"/>
/// </summary>
/// <param name="job">The <see cref="Job"/> to get</param>
/// <returns>A <see cref="Route"/> to the get action</returns>
public static Route GetJob(Job job) => Read<Job>(job.Id);
}
}