using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NuGet.Packaging.Core; using NuGet.Versioning; using nuget_host.Data; using nuget_host.Entities; using nuget_host.Helpers; namespace nuget_host.Controllers { [AllowAnonymous] public class PackagesController : Controller { private readonly ILogger logger; private readonly IDataProtector protector; private readonly NugetSettings nugetSettings; ApplicationDbContext dbContext; public PackagesController( ILoggerFactory loggerFactory, IDataProtectionProvider provider, IOptions nugetOptions, ApplicationDbContext dbContext) { logger = loggerFactory.CreateLogger(); nugetSettings = nugetOptions.Value; protector = provider.CreateProtector(nugetSettings.ProtectionTitle); this.dbContext = dbContext; } [HttpPut("packages")] public async Task Put() { try { var clientVersionId = Request.Headers["X-NuGet-Client-Version"]; var apiKey = Request.Headers["X-NuGet-ApiKey"]; ViewData["versionId"] = typeof(PackagesController).Assembly.FullName; var files = new List(); ViewData["files"] = files; var clearkey = protector.Unprotect(apiKey); var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey); if (apikey == null) { logger.LogError("403 : no api-key"); return Unauthorized(); } foreach (var file in Request.Form.Files) { string initpath = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ?? Environment.GetEnvironmentVariable("TMP") ?? "/tmp", $"nuget_host-{Guid.NewGuid()}.nupkg"); using (FileStream fw = new FileStream(initpath, FileMode.Create)) { file.CopyTo(fw); } using (FileStream fw = new FileStream(initpath, FileMode.Open)) { var archive = new ZipArchive(fw); var nuspec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith(".nuspec")); if (nuspec==null) return BadRequest("no nuspec from archive"); string pkgpath; NuGetVersion version; string pkgid; string fullpath; using (var specstr = nuspec.Open()) { NuspecCoreReader reader = new NuspecCoreReader(specstr); string pkgdesc = reader.GetDescription(); pkgid = reader.GetId(); version = reader.GetVersion(); string pkgidpath = Path.Combine(nugetSettings.PackagesRootDir, pkgid); pkgpath = Path.Combine(pkgidpath, version.ToFullString()); string name = $"{pkgid}-{version}.nupkg"; fullpath = Path.Combine(pkgpath, name); var destpkgiddir = new DirectoryInfo(pkgidpath); Package package = dbContext.Packages.SingleOrDefault(p => p.Id == pkgid); if (package != null) { if (package.OwnerId != apikey.UserId) { return new ForbidResult(); } } else { package = new Package { Id = pkgid, Description = pkgdesc, OwnerId = apikey.UserId }; dbContext.Packages.Add(package); } if (!destpkgiddir.Exists) destpkgiddir.Create(); var source = new FileInfo(initpath); var dest = new FileInfo(fullpath); var destdir = new DirectoryInfo(dest.DirectoryName); if (dest.Exists) { ViewData["msg"] = "existant"; ViewData["ecode"] = 1; logger.LogWarning("400 : existant"); return BadRequest(ViewData); } else { destdir.Create(); source.MoveTo(fullpath); files.Add(name); var newversion = new PackageVersion { Package = package, Major = version.Major, Minor = version.Minor, Patch = version.Patch, IsPrerelease = version.IsPrerelease, FullString = version.ToFullString() }; dbContext.PackageVersions.Add(newversion); await dbContext.SaveChangesAsync(); logger.LogInformation($"new package : {nuspec.Name}"); } } using (var shacrypto = System.Security.Cryptography.SHA512.Create()) { using (var stream = System.IO.File.OpenRead(fullpath)) { var hash = shacrypto.ComputeHash(stream); var shafullname = fullpath + ".sha512"; var hashtext = Convert.ToBase64String(hash); var hashtextbytes = Encoding.ASCII.GetBytes(hashtext); using (var shafile = System.IO.File.OpenWrite(shafullname)) { shafile.Write(hashtextbytes, 0, hashtextbytes.Length); } } } nuspec.ExtractToFile(Path.Combine(pkgpath, pkgid + ".nuspec")); } } return Ok(ViewData); } catch (Exception ex) { logger.LogError(ex.Message); logger.LogError("Stack Trace: "+ ex.StackTrace); return new ObjectResult(new { ViewData, ex.Message}) { StatusCode = 500 }; } } // dotnet add . package -s http://localhost:5000/packages nuget-cli // packages/FindPackagesById()?id='nuget-cli'&semVerLevel=2.0.0 [HttpGet("packages/FindPackagesById()")] public IActionResult Index(string id, string semVerLevel) { if (string.IsNullOrEmpty(id)) { ViewData["msg"] = "no id"; } else { ViewData["id"] = id; // TODO Assert valid sem ver spec var filelst = new DirectoryInfo(nugetSettings.PackagesRootDir); var lst = filelst.GetDirectories(id); ViewData["lst"] = lst.Select(entry => entry.Name); } return Ok(ViewData); } [Authorize] [HttpGet("api/get-key/{*apikey}")] public IActionResult GetApiKey(string apiKey) { return Ok(protector.Protect(apiKey)); } } }