< Summary

Information
Class: LGDXRobotCloud.API.Services.Administration.RobotCertificateService
Assembly: LGDXRobotCloud.API
File(s): /builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Administration/RobotCertificateService.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 118
Coverable lines: 118
Total lines: 166
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 8
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Administration/RobotCertificateService.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2using System.Security.Cryptography.X509Certificates;
 3using LGDXRobotCloud.API.Configurations;
 4using LGDXRobotCloud.API.Exceptions;
 5using LGDXRobotCloud.Data.DbContexts;
 6using LGDXRobotCloud.Data.Entities;
 7using LGDXRobotCloud.Data.Models.Business.Administration;
 8using LGDXRobotCloud.Utilities.Helpers;
 9using Microsoft.EntityFrameworkCore;
 10using Microsoft.Extensions.Options;
 11
 12namespace LGDXRobotCloud.API.Services.Administration;
 13
 14public interface IRobotCertificateService
 15{
 16  Task<(IEnumerable<RobotCertificateListBusinessModel>, PaginationHelper)> GetRobotCertificatesAsync(int pageNumber, int
 17  Task<RobotCertificateBusinessModel> GetRobotCertificateAsync(Guid robotCertificateId);
 18  RobotCertificateIssueBusinessModel IssueRobotCertificate(Guid robotId);
 19  Task<RobotCertificateRenewBusinessModel> RenewRobotCertificateAsync(RobotCertificateRenewRequestBusinessModel robotCer
 20
 21  RootCertificateBusinessModel? GetRootCertificate();
 22}
 23
 024public class RobotCertificateService(
 025    LgdxContext context,
 026    IOptionsSnapshot<LgdxRobotCloudConfiguration> options
 027  ) : IRobotCertificateService
 28{
 029  private readonly LgdxContext _context = context;
 030  private readonly LgdxRobotCloudConfiguration _lgdxRobotCloudConfiguration = options.Value;
 31
 32  private record CertificateDetail
 33  {
 034    required public string RootCertificate { get; set; }
 035    required public string RobotCertificatePrivateKey { get; set; }
 036    required public string RobotCertificatePublicKey { get; set; }
 037    required public string RobotCertificateThumbprint { get; set; }
 038    required public DateTime RobotCertificateNotBefore { get; set; }
 039    required public DateTime RobotCertificateNotAfter { get; set; }
 40  }
 41
 42  public async Task<(IEnumerable<RobotCertificateListBusinessModel>, PaginationHelper)> GetRobotCertificatesAsync(int pa
 043  {
 044    var query = _context.RobotCertificates as IQueryable<RobotCertificate>;
 045    var itemCount = await query.CountAsync();
 046    var PaginationHelper = new PaginationHelper(itemCount, pageNumber, pageSize);
 047    var robotCertificates = await query.AsNoTracking()
 048      .OrderBy(a => a.Id)
 049      .Skip(pageSize * (pageNumber - 1))
 050      .Take(pageSize)
 051      .Select(a => new RobotCertificateListBusinessModel {
 052        Id = a.Id,
 053        Thumbprint = a.Thumbprint,
 054        ThumbprintBackup = a.ThumbprintBackup,
 055        NotBefore = a.NotBefore,
 056        NotAfter = a.NotAfter
 057      })
 058      .ToListAsync();
 059    return (robotCertificates, PaginationHelper);
 060  }
 61
 62  public async Task<RobotCertificateBusinessModel> GetRobotCertificateAsync(Guid robotCertificateId)
 063  {
 064    return await _context.RobotCertificates.AsNoTracking()
 065      .Where(a => a.Id == robotCertificateId)
 066      .Include(a => a.Robot)
 067      .Select(a => new RobotCertificateBusinessModel {
 068        Id = a.Id,
 069        RobotId = a.Robot.Id,
 070        RobotName = a.Robot.Name,
 071        Thumbprint = a.Thumbprint,
 072        ThumbprintBackup = a.ThumbprintBackup,
 073        NotBefore = a.NotBefore,
 074        NotAfter = a.NotAfter
 075      })
 076      .FirstOrDefaultAsync()
 077        ?? throw new LgdxNotFound404Exception();
 078  }
 79
 80  private CertificateDetail GenerateRobotCertificate(Guid robotId)
 081  {
 082    X509Store store = new(StoreName.My, StoreLocation.CurrentUser);
 083    store.Open(OpenFlags.OpenExistingOnly);
 084    X509Certificate2 rootCertificate = store.Certificates.First(c => c.SerialNumber == _lgdxRobotCloudConfiguration.Root
 85
 086    var certificateNotBefore = DateTime.UtcNow;
 087    var certificateNotAfter = DateTimeOffset.UtcNow.AddDays(_lgdxRobotCloudConfiguration.RobotCertificateValidDay);
 88
 089    var rsa = RSA.Create();
 090    var certificateRequest = new CertificateRequest("CN=LGDXRobot Cloud Robot Certificate for " + robotId.ToString() + "
 091    var certificate = certificateRequest.Create(rootCertificate, certificateNotBefore, certificateNotAfter, RandomNumber
 92
 093    return new CertificateDetail {
 094      RootCertificate = rootCertificate.ExportCertificatePem(),
 095      RobotCertificatePrivateKey = new string(PemEncoding.Write("PRIVATE KEY", rsa.ExportPkcs8PrivateKey())),
 096      RobotCertificatePublicKey = certificate.ExportCertificatePem(),
 097      RobotCertificateThumbprint = certificate.Thumbprint,
 098      RobotCertificateNotBefore = certificateNotBefore,
 099      RobotCertificateNotAfter = certificateNotAfter.DateTime
 0100    };
 0101  }
 102
 103  public RobotCertificateIssueBusinessModel IssueRobotCertificate(Guid robotId)
 0104  {
 0105    var certificate = GenerateRobotCertificate(robotId);
 0106    return new RobotCertificateIssueBusinessModel {
 0107      RootCertificate = certificate.RootCertificate,
 0108      RobotCertificatePrivateKey = certificate.RobotCertificatePrivateKey,
 0109      RobotCertificatePublicKey = certificate.RobotCertificatePublicKey,
 0110      RobotCertificateThumbprint = certificate.RobotCertificateThumbprint,
 0111      RobotCertificateNotBefore = certificate.RobotCertificateNotBefore,
 0112      RobotCertificateNotAfter = certificate.RobotCertificateNotAfter
 0113    };
 0114  }
 115
 116  public async Task<RobotCertificateRenewBusinessModel> RenewRobotCertificateAsync(RobotCertificateRenewRequestBusinessM
 0117  {
 0118    var certificate = _context.RobotCertificates
 0119      .Where(c => c.Id == robotCertificateRenewRequestBusinessModel.CertificateId)
 0120      .FirstOrDefault()
 0121        ?? throw new LgdxNotFound404Exception();
 122
 0123      var newCertificate = GenerateRobotCertificate(certificate.RobotId);
 0124      if (robotCertificateRenewRequestBusinessModel.RevokeOldCertificate)
 0125      {
 0126        certificate.ThumbprintBackup = null;
 0127      }
 128      else
 0129      {
 0130        certificate.ThumbprintBackup = certificate.Thumbprint;
 0131      }
 0132      certificate.Thumbprint = newCertificate.RobotCertificateThumbprint;
 0133      certificate.NotBefore = DateTime.SpecifyKind(newCertificate.RobotCertificateNotBefore, DateTimeKind.Utc);
 0134      certificate.NotAfter = DateTime.SpecifyKind(newCertificate.RobotCertificateNotAfter, DateTimeKind.Utc);
 0135      await _context.SaveChangesAsync();
 136
 0137      var robot = await _context.Robots.AsNoTracking()
 0138        .Where(r => r.Id == certificate.RobotId)
 0139        .Select(r => new {
 0140          r.Id,
 0141          r.Name
 0142        })
 0143        .FirstOrDefaultAsync()
 0144          ?? throw new LgdxNotFound404Exception();
 145
 0146      return new RobotCertificateRenewBusinessModel {
 0147        RobotId = robot.Id,
 0148        RobotName = robot.Name,
 0149        RootCertificate = newCertificate.RootCertificate,
 0150        RobotCertificatePrivateKey = newCertificate.RobotCertificatePrivateKey,
 0151        RobotCertificatePublicKey = newCertificate.RobotCertificatePublicKey
 0152      };
 0153    }
 154
 155  public RootCertificateBusinessModel? GetRootCertificate()
 0156  {
 0157    X509Store store = new(StoreName.My, StoreLocation.CurrentUser);
 0158    store.Open(OpenFlags.OpenExistingOnly);
 0159    X509Certificate2 rootCertificate = store.Certificates.First(c => c.SerialNumber == _lgdxRobotCloudConfiguration.Root
 0160    return new RootCertificateBusinessModel {
 0161      NotBefore = rootCertificate.NotBefore.ToUniversalTime(),
 0162      NotAfter = rootCertificate.NotAfter.ToUniversalTime(),
 0163      PublicKey = rootCertificate.ExportCertificatePem()
 0164    };
 0165  }
 166}