From cdc891904e4c1173e98643165cc7b61a8e0803fa Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 30 Jul 2018 01:19:55 -0400 Subject: [PATCH] Oof there's the Update of RepositoryController for ya --- src/Tgstation.Server.Api/Models/Repository.cs | 8 +- .../Models/RevisionInformation.cs | 7 +- .../Components/Repository/IRepository.cs | 18 +- .../Components/Repository/Repository.cs | 30 ++- .../Watchdog/SessionControllerFactory.cs | 6 +- .../Controllers/RepositoryController.cs | 211 ++++++++++++++++-- .../Models/DatabaseContext.cs | 10 +- .../Models/RepositorySettings.cs | 2 +- .../Models/RevInfoTestMerge.cs | 27 +++ .../Models/RevisionInformation.cs | 12 +- src/Tgstation.Server.Host/Models/TestMerge.cs | 18 +- .../Security/WindowsSystemIdentityFactory.cs | 2 +- 12 files changed, 315 insertions(+), 36 deletions(-) create mode 100644 src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs diff --git a/src/Tgstation.Server.Api/Models/Repository.cs b/src/Tgstation.Server.Api/Models/Repository.cs index 13a4b48814..004be123cf 100644 --- a/src/Tgstation.Server.Api/Models/Repository.cs +++ b/src/Tgstation.Server.Api/Models/Repository.cs @@ -24,7 +24,13 @@ namespace Tgstation.Server.Api.Models /// If the repository was cloned from GitHub.com. If this enables test merge functionality /// [Permissions(DenyWrite = true)] - public bool IsGitHub { get; set; } + public bool? IsGitHub { get; set; } + + /// + /// Do the equivalent of a git pull. Will attempt to merge unless is also specified + /// + [Permissions(WriteRight = RepositoryRights.UpdateBranch)] + public bool? UpdateReference { get; set; } /// /// The branch or tag HEAD points to diff --git a/src/Tgstation.Server.Api/Models/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/RevisionInformation.cs index 9a3de97f05..0340478d97 100644 --- a/src/Tgstation.Server.Api/Models/RevisionInformation.cs +++ b/src/Tgstation.Server.Api/Models/RevisionInformation.cs @@ -5,10 +5,15 @@ namespace Tgstation.Server.Api.Models /// public sealed class RevisionInformation : Internal.RevisionInformation { + /// + /// The that was created with this + /// + public TestMerge PrimaryTestMerge { get; set; } + /// /// The s active in the /// - public List TestMerges { get; set; } + public List ActiveTestMerges { get; set; } /// /// The s made from the diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs index 10412a6458..23b6d68b47 100644 --- a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs @@ -14,6 +14,21 @@ namespace Tgstation.Server.Host.Components.Repository /// bool IsGitHubRepository { get; } + /// + /// The if this + /// + string GitHubOwner { get; } + + /// + /// The if this + /// + string GitHubRepoName { get; } + + /// + /// If tracks an upstream branch + /// + bool Tracking { get; } + /// /// The SHA of the HEAD /// @@ -46,9 +61,8 @@ namespace Tgstation.Server.Host.Components.Repository /// The e-mail of the merge committer /// The access string to fetch from the origin repository /// The for the operation - /// A string to identify the user that merged /// A resulting in the SHA of the new HEAD on success, on merge conflict - Task AddTestMerge(int pullRequestNumber, string targetCommit, string committerName, string committerEmail, string accessString, string mergerIdentifier, CancellationToken cancellationToken); + Task AddTestMerge(int pullRequestNumber, string targetCommit, string committerName, string committerEmail, string accessString, CancellationToken cancellationToken); /// /// Fetch commits from the origin repository diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 3e26156303..13fdf97475 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -22,6 +22,15 @@ namespace Tgstation.Server.Host.Components.Repository /// public bool IsGitHubRepository { get; } + /// + public string GitHubOwner { get; } + + /// + public string GitHubRepoName { get; } + + /// + public bool Tracking => repository.Head.IsTracking; + /// public string Head => repository.Head.Tip.Sha; @@ -51,6 +60,19 @@ namespace Tgstation.Server.Host.Components.Repository /// readonly Action onDispose; + static void GetRepositoryOwnerName(string remote, out string owner, out string name) + { + //Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1] + //Yes use .git twice in case it was weird + var toRemove = new string[] { ".git", "/", ".git" }; + foreach (string item in toRemove) + if (remote.EndsWith(item, StringComparison.OrdinalIgnoreCase)) + remote = remote.Substring(0, remote.LastIndexOf(item, StringComparison.OrdinalIgnoreCase)); + var splits = remote.Split('/'); + name = splits[splits.Length - 1]; + owner = splits[splits.Length - 2].Split('.')[0]; + } + /// /// Construct a /// @@ -65,6 +87,12 @@ namespace Tgstation.Server.Host.Components.Repository this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose)); IsGitHubRepository = Origin.ToUpperInvariant().Contains("://GITHUB.COM/"); + if (IsGitHubRepository) + { + GetRepositoryOwnerName(Origin, out var owner, out var name); + GitHubOwner = owner; + GitHubRepoName = name; + } } /// @@ -106,7 +134,7 @@ namespace Tgstation.Server.Host.Components.Repository } /// - public async Task AddTestMerge(int pullRequestNumber, string targetCommit, string committerName, string committerEmail, string accessString, string mergerIdentifier, CancellationToken cancellationToken) + public async Task AddTestMerge(int pullRequestNumber, string targetCommit, string committerName, string committerEmail, string accessString, CancellationToken cancellationToken) { if (!IsGitHubRepository) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index 1a24e7eb71..1afc9a1571 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -116,14 +116,14 @@ namespace Tgstation.Server.Host.Components.Watchdog InstanceName = instance.Name, Revision = dmbProvider.CompileJob.RevisionInformation }; - interopInfo.TestMerges.AddRange(dmbProvider.CompileJob.RevisionInformation.TestMerges.Select(x => new TestMerge + interopInfo.TestMerges.AddRange(dmbProvider.CompileJob.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x => new TestMerge { Author = x.Author, Body = x.BodyAtMerge, Comment = x.Comment, - CommitSha = x.RevisionInformation.CommitSha, + CommitSha = x.PrimaryRevisionInformation.CommitSha, Number = x.Number, - OriginCommitSha = x.RevisionInformation.OriginCommitSha, + OriginCommitSha = x.PrimaryRevisionInformation.OriginCommitSha, PullRequestCommit = x.PullRequestRevision, TimeMerged = x.MergedAt.Ticks, Title = x.TitleAtMerge, diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index 1b19f662cb..bded31639d 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -3,7 +3,9 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Net; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; @@ -15,7 +17,7 @@ using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Controllers { /// - /// Controller for managing the s + /// Controller for managing the s /// [Route("/" + nameof(Repository))] public sealed class RepositoryController : ModelController @@ -25,30 +27,32 @@ namespace Tgstation.Server.Host.Controllers /// readonly IInstanceManager instanceManager; + /// + /// The for the + /// + readonly Octokit.IGitHubClient gitHubClient; + /// /// Construct a /// /// The for the /// The for the /// The value of - public RepositoryController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager) : base(databaseContext, authenticationContextFactory, true) + /// The value of + public RepositoryController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, Octokit.IGitHubClient gitHubClient) : base(databaseContext, authenticationContextFactory, true) { this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + this.gitHubClient = gitHubClient ?? throw new ArgumentNullException(nameof(gitHubClient)); } static string GetAccessString(Api.Models.Internal.RepositorySettings repositorySettings) => repositorySettings.AccessUser != null ? String.Concat(repositorySettings.AccessUser, '@', repositorySettings.AccessToken) : null; - async Task PopulateApi(Repository model, Components.Repository.IRepository repository, string lastOriginCommitSha, CancellationToken cancellationToken) + async Task LoadRevisionInformation(Components.Repository.IRepository repository, Action revInfoSink, CancellationToken cancellationToken) { - model.IsGitHub = repository.IsGitHubRepository; - model.Origin = repository.Origin; - model.Reference = repository.Reference; - model.Sha = repository.Head; - - //rev info stuff - var revisionInfo = await DatabaseContext.RevisionInformations.Where(x => x.CommitSha == model.Sha) + var repoSha = repository.Head; + var revisionInfo = await DatabaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha) .Include(x => x.CompileJobs) - .Include(x => x.TestMerges) //minimal info, they can query the rest if they're allowed + .Include(x => x.ActiveTestMerges) //minimal info, they can query the rest if they're allowed .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); //search every rev info because LOL SHA COLLISIONS var needsDbUpdate = revisionInfo == default; @@ -57,16 +61,30 @@ namespace Tgstation.Server.Host.Controllers //needs insertion revisionInfo = new Models.RevisionInformation { - CommitSha = model.Sha, + CommitSha = repoSha, CompileJobs = new List(), - TestMerges = new List(), //non null vals for api returns - OriginCommitSha = lastOriginCommitSha ?? model.Sha + ActiveTestMerges = new List() //non null vals for api returns }; - DatabaseContext.RevisionInformations.Add(revisionInfo); - await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + lock (DatabaseContext) //cleaner this way + DatabaseContext.RevisionInformations.Add(revisionInfo); } + revInfoSink?.Invoke(revisionInfo); + return needsDbUpdate; + } + async Task PopulateApi(Repository model, Components.Repository.IRepository repository, string lastOriginCommitSha, Action revInfoSink, CancellationToken cancellationToken) + { + model.IsGitHub = repository.IsGitHubRepository; + model.Origin = repository.Origin; + model.Reference = repository.Reference; + model.Sha = repository.Head; + + //rev info stuff + Models.RevisionInformation revisionInfo = null; + var needsDbUpdate = await LoadRevisionInformation(repository, x => revisionInfo = x, cancellationToken).ConfigureAwait(false); + revisionInfo.OriginCommitSha = lastOriginCommitSha ?? model.Sha; + revInfoSink?.Invoke(revisionInfo); model.RevisionInformation = revisionInfo.ToApi(); return needsDbUpdate; } @@ -102,7 +120,7 @@ namespace Tgstation.Server.Host.Controllers //clone conflict return Conflict(); var api = currentModel.ToApi(); - await PopulateApi(api, repo, null, cancellationToken).ConfigureAwait(false); + await PopulateApi(api, repo, null, null, cancellationToken).ConfigureAwait(false); currentModel.LastOriginCommitSha = repo.Head; await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); return Json(api); @@ -146,7 +164,164 @@ namespace Tgstation.Server.Host.Controllers using (var repo = await instanceManager.GetInstance(Instance).RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) { - if (await PopulateApi(api, repo, currentModel.LastOriginCommitSha, cancellationToken).ConfigureAwait(false)) + if (await PopulateApi(api, repo, currentModel.LastOriginCommitSha, null, cancellationToken).ConfigureAwait(false)) + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + return Json(api); + } + } + + /// + [TgsAuthorize(RepositoryRights.ChangeAutoUpdateSettings | RepositoryRights.ChangeCommitter | RepositoryRights.ChangeCredentials | RepositoryRights.ChangeTestMergeCommits | RepositoryRights.MergePullRequest | RepositoryRights.SetReference | RepositoryRights.SetSha | RepositoryRights.UpdateBranch)] + public override async Task Update([FromBody]Repository model, CancellationToken cancellationToken) + { + if (model == null) + return BadRequest(new { message = "Missing request model!" }); + + if (model.AccessUser == null ^ model.AccessToken == null) + return BadRequest(new { message = "Either both accessToken and accessUser must be present or neither!" }); + + if (model.Sha != null && model.Reference != null) + return BadRequest(new { message = "Only one of sha or reference may be specified!" }); + + if(model.Sha != null && model.UpdateReference == true) + return BadRequest(new { message = "Cannot update a reference when checking out a sha!" }); + + if (model.Origin != null) + return BadRequest(new { message = "origin cannot be modified without deleting the repository!" }); + + var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0; + var userRights = (RepositoryRights)AuthenticationContext.GetRight(RightsType.Repository); + if (newTestMerges && !userRights.HasFlag(RepositoryRights.MergePullRequest)) + return Forbid(); + + var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (currentModel == default) + return StatusCode((int)HttpStatusCode.Gone); + + bool CheckModified(Expression> expression, RepositoryRights requiredRight) + { + var memberSelectorExpression = (MemberExpression)expression.Body; + var property = (PropertyInfo)memberSelectorExpression.Member; + + var newVal = property.GetValue(model); + if (newVal == null) + return false; + if (!userRights.HasFlag(requiredRight) && property.GetValue(currentModel) != newVal) + return true; + + property.SetValue(currentModel, newVal); + return false; + }; + + if (CheckModified(x => x.AccessToken, RepositoryRights.ChangeCredentials) + || CheckModified(x => x.AccessUser, RepositoryRights.ChangeCredentials) + || CheckModified(x => x.AutoUpdatesKeepTestMerges, RepositoryRights.ChangeAutoUpdateSettings) + || CheckModified(x => x.AutoUpdatesSynchronize, RepositoryRights.ChangeAutoUpdateSettings) + || CheckModified(x => x.CommitterEmail, RepositoryRights.ChangeCommitter) + || CheckModified(x => x.CommitterName, RepositoryRights.ChangeCommitter) + || CheckModified(x => x.PushTestMergeCommits, RepositoryRights.ChangeTestMergeCommits) + || CheckModified(x => x.ShowTestMergeCommitters, RepositoryRights.ChangeTestMergeCommits)) + return Forbid(); + + //so that's the stuff that is just db, the rest is tricky + + using (var repo = await instanceManager.GetInstance(Instance).RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + if (newTestMerges && !repo.IsGitHubRepository) + return Conflict(new { message = "Cannot test merge on a non GitHub based repository!" }); + + var committerName = currentModel.ShowTestMergeCommitters.Value ? AuthenticationContext.User.Name : currentModel.CommitterName; + var accessString = GetAccessString(currentModel); + if (model.UpdateReference == true) + { + if (!repo.Tracking && model.Reference == null) + return Conflict(new { message = "Not on an updatable reference!" }); + if (!userRights.HasFlag(RepositoryRights.UpdateBranch)) + return Forbid(); + await repo.FetchOrigin(accessString, cancellationToken).ConfigureAwait(false); + if (model.Sha == null && model.Reference == null) + await repo.MergeOrigin(committerName, currentModel.CommitterEmail, cancellationToken).ConfigureAwait(false); + } + + if (model.Sha != null || model.Reference != null) + { + await repo.CheckoutObject(model.Sha ?? model.Reference, cancellationToken).ConfigureAwait(false); + + if (model.UpdateReference == true && model.Reference != null) + { + if (!repo.Tracking) + return Conflict(new { message = "Checked out reference is does not track an object!" }); + await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false); + } + } + + //now add any testmerges + if (newTestMerges) + { + var repoOwner = repo.GitHubOwner; + var repoName = repo.GitHubRepoName; + var allAddedTestMerges = new List(); + foreach (var I in model.NewTestMerges) + { + await repo.AddTestMerge(I.Number, I.PullRequestRevision, committerName, currentModel.CommitterEmail, accessString, cancellationToken).ConfigureAwait(false); + + Octokit.PullRequest pr = null; + string errorMessage = null; + + Models.RevisionInformation revisionInformation = null; + var revInfoTask = LoadRevisionInformation(repo, x => revisionInformation = x, cancellationToken); + + try + { + pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number).ConfigureAwait(false); + } + catch (Octokit.RateLimitExceededException) + { + //you look at your anonymous access and sigh + errorMessage = "PRE RATE LIMITED"; + } + catch (Octokit.NotFoundException) + { + //you look at your shithub access and sigh + errorMessage = "PRE NOT FOUND"; + } + + var attachedContextUser = new Models.User + { + Id = AuthenticationContext.User.Id + }; + DatabaseContext.Users.Attach(attachedContextUser); + + var tm = new Models.TestMerge + { + Author = pr?.User.Login ?? errorMessage, + BodyAtMerge = pr?.Body, + MergedAt = DateTimeOffset.Now, + TitleAtMerge = pr?.Title ?? errorMessage, + Comment = I.Comment, + Number = I.Number, + MergedBy = attachedContextUser, + PullRequestRevision = I.PullRequestRevision, + Url = pr?.HtmlUrl ?? errorMessage + }; + if (pr == null) + tm.BodyAtMerge = errorMessage; + + allAddedTestMerges.Add(tm); + + await revInfoTask.ConfigureAwait(false); + + revisionInformation.PrimaryTestMerge = tm; + revisionInformation.ActiveTestMerges.AddRange(allAddedTestMerges.Select(x => new RevInfoTestMerge + { + TestMerge = x + })); + } + } + + var api = currentModel.ToApi(); + if (await PopulateApi(api, repo, currentModel.LastOriginCommitSha, null, cancellationToken).ConfigureAwait(false) || newTestMerges) await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); return Json(api); } diff --git a/src/Tgstation.Server.Host/Models/DatabaseContext.cs b/src/Tgstation.Server.Host/Models/DatabaseContext.cs index 5c52b6d5f6..52a16491a4 100644 --- a/src/Tgstation.Server.Host/Models/DatabaseContext.cs +++ b/src/Tgstation.Server.Host/Models/DatabaseContext.cs @@ -56,6 +56,11 @@ namespace Tgstation.Server.Host.Models /// public DbSet ReattachInformations { get; set; } + /// + /// The s om the + /// + public DbSet RevInfoTestMerges { get; set; } + /// public DbSet WatchdogReattachInformations { get; set; } @@ -96,9 +101,12 @@ namespace Tgstation.Server.Host.Models modelBuilder.Entity().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique(); + modelBuilder.Entity().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.Cascade); + var revInfo = modelBuilder.Entity(); revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade); - revInfo.HasMany(x => x.TestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade); + revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade); + revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.SetNull); revInfo.HasIndex(x => x.CommitSha).IsUnique(); var chatChannel = modelBuilder.Entity(); diff --git a/src/Tgstation.Server.Host/Models/RepositorySettings.cs b/src/Tgstation.Server.Host/Models/RepositorySettings.cs index 2a9eef753e..18fda50bfb 100644 --- a/src/Tgstation.Server.Host/Models/RepositorySettings.cs +++ b/src/Tgstation.Server.Host/Models/RepositorySettings.cs @@ -24,7 +24,7 @@ namespace Tgstation.Server.Host.Models /// public Repository ToApi() => new Repository { - AccessToken = AccessToken, + //AccessToken = AccessToken, //never show this AccessUser = AccessUser, AutoUpdatesKeepTestMerges = AutoUpdatesKeepTestMerges, AutoUpdatesSynchronize = AutoUpdatesSynchronize, diff --git a/src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs b/src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs new file mode 100644 index 0000000000..826b124780 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Many to many relationship for and + /// + public sealed class RevInfoTestMerge + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The + /// + [Required] + public TestMerge TestMerge { get; set; } + + /// + /// The + /// + [Required] + public RevisionInformation RevisionInformation { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Models/RevisionInformation.cs b/src/Tgstation.Server.Host/Models/RevisionInformation.cs index 23db58d0f8..4ff7c0bd79 100644 --- a/src/Tgstation.Server.Host/Models/RevisionInformation.cs +++ b/src/Tgstation.Server.Host/Models/RevisionInformation.cs @@ -17,9 +17,14 @@ namespace Tgstation.Server.Host.Models public Instance Instance { get; set; } /// - /// See + /// See /// - public List TestMerges { get; set; } + public TestMerge PrimaryTestMerge { get; set; } + + /// + /// See + /// + public List ActiveTestMerges { get; set; } /// /// See s made from this @@ -31,7 +36,8 @@ namespace Tgstation.Server.Host.Models { CommitSha = CommitSha, OriginCommitSha = OriginCommitSha, - TestMerges = TestMerges.Select(x => x.ToApi()).ToList(), + PrimaryTestMerge = PrimaryTestMerge?.ToApi(), + ActiveTestMerges = ActiveTestMerges.Select(x => x.TestMerge.ToApi()).ToList(), CompileJobs = CompileJobs.Select(x => x.ToApi()).ToList() }; } diff --git a/src/Tgstation.Server.Host/Models/TestMerge.cs b/src/Tgstation.Server.Host/Models/TestMerge.cs index c4f6404f31..2428a192a5 100644 --- a/src/Tgstation.Server.Host/Models/TestMerge.cs +++ b/src/Tgstation.Server.Host/Models/TestMerge.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Host.Models { @@ -12,10 +13,19 @@ namespace Tgstation.Server.Host.Models public User MergedBy { get; set; } /// - /// The for the + /// The initial the was merged with /// - [Required] - public RevisionInformation RevisionInformation { get; set; } + public RevisionInformation PrimaryRevisionInformation { get; set; } + + /// + /// Foreign key for + /// + public long? PrimaryRevisionInformationId { get; set; } + + /// + /// All the for the + /// + public List RevisonInformations { get; set; } /// public Api.Models.TestMerge ToApi() => new Api.Models.TestMerge diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs index 871269f3ae..7c4aa091f2 100644 --- a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs @@ -64,7 +64,7 @@ namespace Tgstation.Server.Host.Security var res = NativeMethods.LogonUser(splits.Length > 1 ? splits[1] : splits[0], splits.Length > 1 ? splits[0] : null, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token); if (!res) - throw new Win32Exception(Marshal.GetLastWin32Error()); + return null; using (var handle = new SafeAccessTokenHandle(token)) //checked internally, windows identity always duplicates the handle when constructed with a userToken return (ISystemIdentity)new WindowsSystemIdentity(new WindowsIdentity(handle.DangerousGetHandle())); //https://github.com/dotnet/corefx/blob/6ed61acebe3214fcf79b4274f2bb9b55c0604a4d/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs#L271