mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-26 14:37:44 +01:00
More attribute stuff
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user