Add InfisicalScepMdmProfile model with ToSyncMl() builder
POCO mirrors the Windows ClientCertificateInstall/SCEP CSP node set (ServerURL, Challenge, SubjectName, SubjectAlternativeNames, EKUMapping, KeyUsage, KeyLength, KeyAlgorithm, HashAlgorithm, KeyProtection, ContainerName, ValidPeriod, ValidPeriodUnits, RetryCount, RetryDelay, TemplateName, CAThumbprint, CustomTextToShowInPrompt) plus a Scope hint (Device or User) and a UniqueId for the CSP path segment. ToSyncMl() builds an Atomic SyncBody of Replace operations and a trailing Exec on Install/Enroll using XDocument, serializes through XmlWriter with explicit settings (UTF-8 no BOM, indented, no BOM, Replace newline handling), then round-trip-validates through XmlReader before returning the string.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace PSInfisicalAPI.Models
|
||||
{
|
||||
public sealed class InfisicalScepMdmProfile
|
||||
{
|
||||
private const string SyncMlMetInfNamespace = "syncml:metinf";
|
||||
|
||||
public string UniqueId { get; set; }
|
||||
public string Scope { get; set; }
|
||||
|
||||
public string ServerUrl { get; set; }
|
||||
public string Challenge { get; set; }
|
||||
|
||||
public string SubjectName { get; set; }
|
||||
public string SubjectAlternativeNames { get; set; }
|
||||
public string EkuMapping { get; set; }
|
||||
public int? KeyUsage { get; set; }
|
||||
|
||||
public int? KeyLength { get; set; }
|
||||
public string KeyAlgorithm { get; set; }
|
||||
public string HashAlgorithm { get; set; }
|
||||
public int? KeyProtection { get; set; }
|
||||
public string ContainerName { get; set; }
|
||||
|
||||
public string ValidPeriod { get; set; }
|
||||
public int? ValidPeriodUnits { get; set; }
|
||||
public int? RetryCount { get; set; }
|
||||
public int? RetryDelay { get; set; }
|
||||
|
||||
public string TemplateName { get; set; }
|
||||
public string CAThumbprint { get; set; }
|
||||
public string CustomTextToShowInPrompt { get; set; }
|
||||
|
||||
public string SourceProfileId { get; set; }
|
||||
public string SourceProfileSlug { get; set; }
|
||||
|
||||
public string ToSyncMl()
|
||||
{
|
||||
if (string.IsNullOrEmpty(UniqueId)) { throw new InvalidOperationException("UniqueId is required."); }
|
||||
if (string.IsNullOrEmpty(ServerUrl)) { throw new InvalidOperationException("ServerUrl is required."); }
|
||||
|
||||
string scopeSegment = string.Equals(Scope, "User", StringComparison.OrdinalIgnoreCase) ? "./User" : "./Device";
|
||||
string nodeBase = string.Concat(scopeSegment, "/Vendor/MSFT/ClientCertificateInstall/SCEP/", UniqueId, "/Install/");
|
||||
|
||||
List<CspNode> nodes = new List<CspNode>();
|
||||
AddString(nodes, "ServerURL", ServerUrl);
|
||||
AddString(nodes, "Challenge", Challenge);
|
||||
AddString(nodes, "SubjectName", SubjectName);
|
||||
AddString(nodes, "SubjectAlternativeNames", SubjectAlternativeNames);
|
||||
AddString(nodes, "EKUMapping", EkuMapping);
|
||||
AddInt(nodes, "KeyUsage", KeyUsage);
|
||||
AddInt(nodes, "KeyLength", KeyLength);
|
||||
AddString(nodes, "KeyAlgorithm", KeyAlgorithm);
|
||||
AddString(nodes, "HashAlgorithm", HashAlgorithm);
|
||||
AddInt(nodes, "KeyProtection", KeyProtection);
|
||||
AddString(nodes, "ContainerName", ContainerName);
|
||||
AddString(nodes, "ValidPeriod", ValidPeriod);
|
||||
AddInt(nodes, "ValidPeriodUnits", ValidPeriodUnits);
|
||||
AddInt(nodes, "RetryCount", RetryCount);
|
||||
AddInt(nodes, "RetryDelay", RetryDelay);
|
||||
AddString(nodes, "TemplateName", TemplateName);
|
||||
AddString(nodes, "CAThumbprint", CAThumbprint);
|
||||
AddString(nodes, "CustomTextToShowInPrompt", CustomTextToShowInPrompt);
|
||||
|
||||
XDocument document = new XDocument(new XDeclaration("1.0", "utf-8", null));
|
||||
XElement syncBody = new XElement("SyncBody");
|
||||
XElement atomic = new XElement("Atomic", new XElement("CmdID", "1"));
|
||||
|
||||
int cmdId = 2;
|
||||
foreach (CspNode node in nodes)
|
||||
{
|
||||
XElement meta = new XElement("Meta", new XElement(XName.Get("Format", SyncMlMetInfNamespace), node.Format));
|
||||
XElement item = new XElement("Item",
|
||||
new XElement("Target", new XElement("LocURI", string.Concat(nodeBase, node.Suffix))),
|
||||
meta,
|
||||
new XElement("Data", node.Value));
|
||||
atomic.Add(new XElement("Replace", new XElement("CmdID", cmdId.ToString(System.Globalization.CultureInfo.InvariantCulture)), item));
|
||||
cmdId++;
|
||||
}
|
||||
|
||||
XElement enrollItem = new XElement("Item",
|
||||
new XElement("Target", new XElement("LocURI", string.Concat(nodeBase, "Enroll"))),
|
||||
new XElement("Meta", new XElement(XName.Get("Format", SyncMlMetInfNamespace), "node")));
|
||||
atomic.Add(new XElement("Exec", new XElement("CmdID", cmdId.ToString(System.Globalization.CultureInfo.InvariantCulture)), enrollItem));
|
||||
|
||||
syncBody.Add(atomic);
|
||||
document.Add(syncBody);
|
||||
|
||||
XmlWriterSettings writerSettings = new XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
IndentChars = " ",
|
||||
NewLineHandling = NewLineHandling.Replace,
|
||||
Encoding = new UTF8Encoding(false),
|
||||
OmitXmlDeclaration = false,
|
||||
CloseOutput = false
|
||||
};
|
||||
|
||||
string serialized;
|
||||
using (MemoryStream buffer = new MemoryStream())
|
||||
{
|
||||
using (XmlWriter writer = XmlWriter.Create(buffer, writerSettings))
|
||||
{
|
||||
document.Save(writer);
|
||||
}
|
||||
serialized = writerSettings.Encoding.GetString(buffer.ToArray());
|
||||
}
|
||||
|
||||
using (StringReader stringReader = new StringReader(serialized))
|
||||
{
|
||||
XmlReaderSettings readerSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit, XmlResolver = null };
|
||||
using (XmlReader reader = XmlReader.Create(stringReader, readerSettings))
|
||||
{
|
||||
XDocument.Load(reader);
|
||||
}
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
private static void AddString(List<CspNode> nodes, string suffix, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) { return; }
|
||||
nodes.Add(new CspNode { Suffix = suffix, Value = value, Format = "chr" });
|
||||
}
|
||||
|
||||
private static void AddInt(List<CspNode> nodes, string suffix, int? value)
|
||||
{
|
||||
if (!value.HasValue) { return; }
|
||||
nodes.Add(new CspNode { Suffix = suffix, Value = value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture), Format = "int" });
|
||||
}
|
||||
|
||||
private sealed class CspNode
|
||||
{
|
||||
public string Suffix;
|
||||
public string Value;
|
||||
public string Format;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user