Thursday, June 17, 2021
Visual Studio / Resharper - Key Board Shortcuts
- Add New File: CTRL + SHIFT + A
- GOTO Solution Explorer: CTRL + ALT + L
Friday, June 4, 2021
EPiServer Search - Not starting
Add this to web.config handlers.
<remove name="svc-Integrated-4.0" />
<add name="svc-Integrated-4.0" path="*.svc" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />
Remove anything that can interfere with 404.<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" path="/404" responseMode="ExecuteURL" />
</httpErrors>
Good guide
https://world.episerver.com/documentation/developer-guides/CMS/search/full-text-search-legacy/Installing-and-deploying-Search-Service/
https://sveinaandahl.blogspot.com/2013/06/how-to-install-episerver-search-for.html
Good tool
https://gregwiechec.com/2015/09/episerver-search-diagnostic-tool/
Thursday, June 3, 2021
C# - SAML Example
using System; using System.Collections.Generic; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Xml; namespace UI.Business.SAML { public class SamlService { private XmlDocument _samlXmlDocument; private XmlNamespaceManager _manager; public void LoadXml(string xml) { _samlXmlDocument = new XmlDocument {PreserveWhitespace = true, XmlResolver = null}; _samlXmlDocument.LoadXml(xml); LoadNamespaceManager(); } public void LoadXmlFromBase64(string response) { System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); LoadXml(enc.GetString(Convert.FromBase64String(response))); } private void LoadNamespaceManager() { _manager = new XmlNamespaceManager(_samlXmlDocument.NameTable); _manager.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); _manager.AddNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); _manager.AddNamespace("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); } public bool IsValid() { bool status = false; // The date is skew to allow the idp clock and this server click to be abit out of sync without failing the validation. var skewDateForValidation = DateTime.Now.AddSeconds(20); XmlNodeList nodeList = _samlXmlDocument.SelectNodes("//ds:Signature", _manager); if (nodeList != null) { status = true; SignedXml signedXml = new SignedXml(_samlXmlDocument); signedXml.LoadXml((XmlElement) nodeList[0]); status &= signedXml.CheckSignature(GetCertificate(), true); var notBefore = NotBefore(); status &= !notBefore.HasValue || (notBefore <= skewDateForValidation); var notOnOrAfter = NotOnOrAfter(); status &= !notOnOrAfter.HasValue || (notOnOrAfter > skewDateForValidation); } return status; } public X509Certificate2 GetCertificate() { using (var store = new X509Store(StoreLocation.LocalMachine)) { store.Open(OpenFlags.ReadOnly); X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByIssuerDistinguishedName, "L=Sachsen, CN=www.staffbase.com, O=Staffbase GmbH, C=DE", false); return certs.Count > 0 ? certs[0] : null; } } public DateTime? NotBefore() { var nodes = _samlXmlDocument.SelectNodes("/samlp:Response/saml:Assertion/saml:Conditions", _manager); string value = null; if (nodes != null && nodes.Count > 0 && nodes[0]?.Attributes?["NotBefore"] != null) { value = nodes[0].Attributes["NotBefore"].Value; } return value != null ? DateTime.Parse(value) : (DateTime?) null; } public DateTime? NotOnOrAfter() { var nodes = _samlXmlDocument.SelectNodes("/samlp:Response/saml:Assertion/saml:Conditions", _manager); string value = null; if (nodes != null && nodes.Count > 0 && nodes[0]?.Attributes?["NotOnOrAfter"] != null) { value = nodes[0].Attributes["NotOnOrAfter"].Value; } return value != null ? DateTime.Parse(value) : (DateTime?) null; } public List<Claim> GetStaffbaseClaims() { var claimsNs = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/"; var nameId = GetCustomAttribute(claimsNs + "nameidentifier"); var givenName = GetCustomAttribute(claimsNs + "givenname"); var surName = GetCustomAttribute(claimsNs + "surname"); var workplaceCode = GetCustomAttribute("arbetsplatskod"); var occupationCode = GetCustomAttribute("yrkeskod"); var employeeNumber = GetCustomAttribute("employeeNumber"); var claims = new List<Claim>(); if (!string.IsNullOrWhiteSpace(givenName) || !string.IsNullOrWhiteSpace(surName)) claims.Add(new Claim(ClaimTypes.Name, $"{givenName} {surName}")); if (!string.IsNullOrWhiteSpace(nameId)) claims.Add(new Claim(ClaimTypes.NameIdentifier, nameId)); // Used as a validation that the user we get in claims is an AD user. If not we do not give it the role "Intranet user" if (!string.IsNullOrWhiteSpace(employeeNumber)) claims.Add(new Claim(ClaimTypes.WindowsAccountName, employeeNumber)); // Set to workplace code is set to Upn claim because the rest of the site depends on it there if (!string.IsNullOrWhiteSpace(workplaceCode)) claims.Add(new Claim(ClaimTypes.Upn, workplaceCode)); // Set to occupationcode code is set to surname claim because the rest of the site depends on it there if (!string.IsNullOrWhiteSpace(occupationCode)) claims.Add(new Claim(ClaimTypes.Surname, occupationCode)); return claims; } public string GetCustomAttribute(string attr) { XmlNode node = _samlXmlDocument.SelectSingleNode("/samlp:Response/saml:Assertion/saml:AttributeStatement/saml:Attribute[@Name='" + attr + "']/saml:AttributeValue", _manager); return node?.InnerText; } public string GetNameId() { XmlNode node = _samlXmlDocument.SelectSingleNode("/samlp:Response/saml:Assertion/saml:Subject/saml:NameID", _manager); return node?.InnerText; } } }

