Wednesday, 23 July 2014

TIL - Hidding HTML elements with jQuery in IE.

The snippets of code below are equivalent, however the code in red does not work in IE (at least IE 9 and 11)

$('#myid').prop('hidden', true);
$('#myid').hide();

$('#myid').prop('hidden', false);
$('#myid').show();

Monday, 21 July 2014

Update Entity from JavaScript in Ms Dynamics CRM 2011/2013 using OData endpoint

Posting this for future reference:

function updateEntity(entityName, id, entity ) {

    var url = oDataUrl + "/" + entityName + "Set(guid'" + id + "')";

    entityData = window.JSON.stringify(entity);

    return $.ajax({
        type: "POST",
        contentType: "application/json;charset=utf-8",
        datatype: "json",
        data: entityData,
        url: url,
        beforeSend: function (x) {
            x.setRequestHeader("Accept", "application/json");
            x.setRequestHeader("X-HTTP-Method", "MERGE")
        },
    });
}
entityName is the logical entity name.
id is the Id of the record that we want to update
entity is a object that contains that values that need changing.
 e.g.:
 account = {};
 account.Name = "New Name"; 

 oDataUrl is the Url of the OData endpoint for your organization

Tuesday, 15 July 2014

RHEL 7 - RHCE + RHCSA Exam Objectives

I intend to go through these exams at some point in the near future and I thought it would be handy to have the objectives for both exams here.

Note: I'm reusing the links from my RHEL 6 post, so if something doesn't quite work let me know and I'll update the post with a new RHEL 7 post. I do intend to go through them but it might take me a while.

I'll attempt to link newer post to the relevant objective, where appropriate. Almost always, it won't be appropriate and in that case I have made an attempt to link to a page with the relevant information for the objective.

Feel free to suggest a better or alternatively link to ones I have provided.

RHCSA Exam Objectives

Red Hat reserves the right to add, modify and remove objectives. Such changes will be made public in advance through revisions to this document.

RHCSA exam candidates should be able to accomplish the tasks below without assistance. These have been grouped into several categories.

Understand and Use Essential Tools

* Access a shell prompt and issue commands with correct syntax
Use input-output redirection (>, >>, |, 2>, etc.)
Use grep and regular expressions to analyze text
Access remote systems using ssh and VNC
* Log in and switch users in multi-user runlevels
* Archive, compress, unpack and uncompress files using tarstargzip, and bzip2
Create and edit text files
Create, delete, copy and move files and directories
Create hard and soft links
* List, set and change standard ugo/rwx permissions
Locate, read and use system documentation including man, info, and files in /usr/share/doc .
[Note: Red Hat may use applications during the exam that are not included in Red Hat Enterprise Linux for the purpose of evaluating candidate's abilities to meet this objective.]

Operate Running Systems

Boot, reboot, and shut down a system normally
Boot systems into different runlevels manually
* Interrupt the boot process in order to gain access to a system
Identify CPU/memory intensive processesadjust process priority with renice, and kill processes
Locate and interpret system log files and journals.
Access a virtual machine's console
Start and stop virtual machines
* Start, stop and check the status of network services
* Securely transfer files between systems.

Configure Local Storage

* List, create, delete partitions on MBR and GPT disks.
Create and remove physical volumes, assign physical volumes to volume groups, create and delete logical volumes
Create and configure LUKS-encrypted partitions and logical volumes to prompt for password and mount a decrypted file system at boot
Configure systems to mount file systems at boot by Universally Unique ID (UUID) or label
Add new partitions, logical volumes and swap to a system non-destructively

Create and Configure File Systems

* Create, mount, unmount, and use vfat, ext4 and xfs file systems.
Mount, unmount and use LUKS-encrypted file systems
Mount and unmount CIFS and NFS network file systems
Configure systems to mount ext4, LUKS-encrypted and network file systems automatically
* Extend existing unencrypted logical volumes
Create and configure set-GID directories for collaboration
Create and manage Access Control Lists (ACLs)
Diagnose and correct file permission problems

Deploy, Configure and Maintain Systems

Configure networking and hostname resolution statically or dynamically
* Schedule tasks using at and cron
* Start and stop services and configure services to start automatically at boot
Configure systems to boot into a specific runlevel automatically
Install Red Hat Enterprise Linux automatically using Kickstart
Configure a physical machine to host virtual guests
Install Red Hat Enterprise Linux systems as virtual guests
Configure systems to launch virtual machines at boot
* Configure a system to use time services.
* Install and update software packages from Red Hat Network, a remote repository, or from the local filesystem
Update the kernel package appropriately to ensure a bootable system
Modify the system bootloader

Manage Users and Groups

Create, delete, and modify local user accounts
Change passwords and adjust password aging for local user accounts
Create, delete and modify local groups and group memberships
Configure a system to use an existing LDAP directory service for user and group information
* Configure a system to use an existing authentication service for user and group information.

Manage Security

Configure firewall settings using system-config-firewall or iptables
Configure key-based authentication for SSH
Set enforcing and permissive modes for SELinux
* List and identify SELinux file and process context
* Restore default file contexts
Use boolean settings to modify system SELinux settings
Diagnose and address routine SELinux policy violations

RHCE Exam Objectives

Red Hat reserves the right to add, modify and remove objectives. Such changes will be made public in advance through revisions to this document.

RHCE exam candidates should be able to accomplish the following without assistance. These have been grouped into several categories.
System Configuration and Management

* Use network teaming or bonding to configure aggregated network links between two Red Hat Enterprise Linux systems.
* Configure IPv6 addresses and perform basic IPv6 troubleshooting
Route IP traffic and create static routes
* Use FirewallD, including Rich Rules, Zones and custom rules, to implement packet filtering and configure network address translation (NAT).
Use /proc/sys and sysctl to modify and set kernel run-time parameters
Configure system to authenticate using Kerberos
Configure a system as an iSCSI initiator that persistently mounts an iSCSI target
Produce and deliver reports on system utilization (processor, memory, disk, and network)
Use shell scripting to automate system maintenance tasks
Configure a system to log to a remote system
Configure a system to accept logging from a remote system

Network Services

Network services are an important subset of the exam objectives. RHCE candidates should be capable of meeting the following objectives for each of the network services listed below:

* Install the packages needed to provide the service
* Configure SELinux to support the service
Use SELinux port labelling to allow services to use non-standard ports.
* Configure the service to start when the system is booted
* Configure the service for basic operation
* Configure host-based and user-based security for the service

RHCE candidates should also be capable of meeting the following objectives associated with specific services:

HTTP/HTTPS

Configure a virtual host
Configure private directories
Deploy a basic CGI application
Configure group-managed content
Configure TLS security

DNS

Configure a caching-only name server
Configure a caching-only name server to forward DNS queries

NFS

Provide network shares to specific clients
Provide network shares suitable for group collaboration
* Use Kerberos to control access to NFS network shares.

SMB

Provide network shares to specific clients
Provide network shares suitable for group collaboration

SMTP

* Configure a system to forward all email to a central mail server

SSH

Configure key-based authentication
Configure additional options described in documentation

NTP

Synchronize time using other NTP peers


Database Services

* Install and configure MariaDB.
* Backup and restore a database.
* Create a simple database schema.
* Perform simple SQL queries against a database.

Monday, 14 July 2014

The authentication endpoint Kerberos was not found on the configured Secure Token Service!

We finally managed to overcome all issues and deployed the build to the OAT environment and it went: KABOOM!!!
Unable to get item. Exception: System.NotSupportedException: The authentication endpoint Kerberos was not found on the configured Secure Token Service! at Microsoft.Xrm.Sdk.Client.IssuerEndpointDictionary.GetIssuerEndpoint(TokenServiceCredentialType credentialType) at Microsoft.Xrm.Sdk.Client.AuthenticationCredentials.get_IssuerEndpoint() at Microsoft.Xrm.Sdk.Client.ServiceConfiguration`1.AuthenticateInternal(AuthenticationCredentials authenticationCredentials) at Microsoft.Xrm.Sdk.Client.ServiceConfiguration`1.AuthenticateFederationInternal(AuthenticationCredentials authenticationCredentials) at Microsoft.Xrm.Sdk.Client.ServiceConfiguration`1.Authenticate(AuthenticationCredentials authenticationCredentials) at Microsoft.Xrm.Sdk.Client.ServiceConfiguration`1.Authenticate(ClientCredentials clientCredentials) at Microsoft.Xrm.Sdk.Client.OrganizationServiceConfiguration.Authenticate(ClientCredentials clientCredentials) at Microsoft.Xrm.Sdk.Client.ServiceProxy`1.AuthenticateClaims() at Microsoft.Xrm.Sdk.Client.ServiceProxy`1.AuthenticateCore() at Microsoft.Xrm.Sdk.Client.ServiceProxy`1.Authenticate() at Microsoft.Xrm.Sdk.Client.ServiceProxy`1.ValidateAuthentication() at Microsoft.Xrm.Sdk.Client.ServiceContextInitializer`1.Initialize(ServiceProxy`1 proxy) at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.RetrieveMultipleCore(QueryBase query) at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.RetrieveMultiple(QueryBase query) at Consumer.ItemManager.RetrieveLastItem(String type) at
Consumer.ItemManager.RetrieveLastItem(String type) at
Consumer.Service.ConsumerService.Process[T](Config feed)

I thought that the Kerberos endpoint must not be enabled in ADFS, but it was and after a bit of investigating, it turns out that this is a known issue in MS Dynamics CRM 2011/13.

The interesting bit about this issue is that the front end was working fine, but trying to use the SDK in any way was not working.

The MEX endpoint that gets set when Claims Based Authentication is configured is like this:
https://adfs.domain.com/adfs/ls/mex
This is a bit of a problem, as it doesn't exist :(

The Working MEX endpoint is:
https://adfs.domain.com/adfs/services/trust/mex
Microsoft have kindly provided a PowerShell script to rectify this issue:

Save this as UpdateMEXEndpoint.ps1

Param (
     #optional params
     [string]$ConfigurationEntityName="FederationProvider",
     [string]$SettingName="ActiveMexEndpoint",
     [object]$SettingValue,
     [Guid]$Id
 )
 $RemoveSnapInWhenDone = $False
 
 if (-not (Get-PSSnapin -Name Microsoft.Crm.PowerShell -ErrorAction SilentlyContinue))
 {
     Add-PSSnapin Microsoft.Crm.PowerShell
     $RemoveSnapInWhenDone = $True
 }
 
 $Id=(Get-CrmAdvancedSetting -ConfigurationEntityName FederationProvider -Setting ActiveMexEndpoint).Attributes[0].Value
 
 $setting = New-Object "Microsoft.Xrm.Sdk.Deployment.ConfigurationEntity"
 $setting.LogicalName = $ConfigurationEntityName
 if($Id) { $setting.Id = $Id }
 
 $setting.Attributes = New-Object "Microsoft.Xrm.Sdk.Deployment.AttributeCollection"
 $keypair = New-Object "System.Collections.Generic.KeyValuePair[String, Object]" ($SettingName, $SettingValue)
 $setting.Attributes.Add($keypair)
 
 Set-CrmAdvancedSetting -Entity $setting
 
 if($RemoveSnapInWhenDone)
 {
     Remove-PSSnapin Microsoft.Crm.PowerShell
 }

This can then be used to modify the relevant setting:

UpdateMEXEndpoint.ps1 –SettingValue “https://<ADFS STSHOST>/adfs/services/trust/mex”

An alternative to use this script is updating the FederationProvider table in the MSCRM_Config database, but this is not supported.

Monday, 7 July 2014

Storing sensitive data (e.g. passwords) in MS Dynamics CRM 2011/2013

We need to integrate with a third party, who have decided that implementing a federated trust is too complex and thus have just given us a user name and password.

Since the integration is done via a couple of plug-ins and custom activities we've decided to store the password in Dynamics CRM, the only problem is that there is no out of the box way of storing the passwords that would allow a relatively simple automated deployment.

Sure, we can register plug-in and use the secure configuration but that means deploying the solution and then re-registering the plug-ins with the secure data but this wasn't suitable.

We ruled out symmetric encryption as we would just have the same problem but for the encryption key, so the obvious choice was asymmetric encryption, the problem is that asymmetric encryption is not really suitable for large amounts of data, so we settled on the recommend way of using asymmetric encryption to encrypt the encryption key of a symmetric encryption scheme.

The thing is, the .NET framework sort of includes this in the form of the EncryptedXml class, which can use a certificate to encrypt and decrypt Xml documents and we can use this for storing a password for instance.

A sample of how to use this class can be seen below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using log4net;

namespace Encryption
{
    public class Encryption
    {
        ILog logger;
        X509Certificate2 certificate;

        public Encryption(string certificateThumbprint) 
        {
            logger = LogManager.GetLogger("Encryption");
            certificate = GetCert(certificateThumbprint);
        }

        public string Encrypt(string plaintext)
        {
            XmlDocument Doc = new XmlDocument();
            Doc.LoadXml(string.Format("<sensitivedata>{0}</sensitivedata>", HttpUtility.HtmlEncode(plaintext)));
            Doc.PreserveWhitespace = true;
            XmlElement toEncrypt = Doc.GetElementsByTagName("sensitivedata")[0] as XmlElement;
            EncryptedXml eXml = new EncryptedXml();
            EncryptedData edElement = eXml.Encrypt(toEncrypt, certificate);
            EncryptedXml.ReplaceElement(toEncrypt, edElement, false);
            return Doc.OuterXml;
        }

        public string Decrypt(string encryptedtext)
        {
            XmlDocument Doc = new XmlDocument();
            Doc.LoadXml(encryptedtext);
            EncryptedXml exml = new EncryptedXml(Doc);
            exml.DecryptDocument();
            string plaintext = HttpUtility.HtmlDecode(XDocument.Parse(Doc.OuterXml).Element("sensitivedata").Value);
            return plaintext;
        }

        private X509Certificate2 GetCert(string thumbprint)
        {
            X509Certificate2 cert = null;
            X509Store store = new X509Store(StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadOnly);
            try
            {
                X509Certificate2Collection certCollection = store.Certificates;
                cert = certCollection.Cast<X509Certificate2>().Where(c => c.Thumbprint.Equals(thumbprint)).Single();
            }
            catch (Exception ex)
            {
                logger.ErrorFormat("An error occurred looking for certificate with thumbprint: {0}.\nException:{1}."
                    , thumbprint, ex);
            }
            finally
            {
                store.Close();
            }
   
            return cert;
        }
    }
}

The thing to note is that sensitive data to be encrypted needs to be valid xml data, in other words, if your sensitive data contains ampersands it will not be parsed, which is why I use the HttpUtility class to encode and decode the sensitive data.
 
 It does seem a little bit fiddly but it is quite almost all done by the framework, so it's really less code to maintain and the extra text that needs to be stored is not a consideration as it's a single record.
 
Furthermore, it would be trivial to modify and return a plaintext Xml Document for processing instead of the value of an element, but the value of an element is what I needed.

Saturday, 5 July 2014

URLs fields in MS Dynamics CRM 2011 - MS Dynamics CRM 2011 annoyances part 1337

We had a requirement to let users see a URL field but not change the URL, the requirement, quite sensibly specified that the users should be able to click on the link.
I thought this was a textbook case for field security profiles, alas I was wrong. In MS Dynamics CRM 2011 if a URL field is read only, however this is achieved, the URL will not be clickable.

Despite the message, it does not work, note how the mouse icon does not change.


Fortunately, this has been rectified in MS Dynamics CRM 2013

Tuesday, 1 July 2014

ADFS Sign-in Page Url

Another happy day messing about with ADFS, another day I needed this, so here it goes:
https://<ADFS FQDN>/adfs/ls/IdpInitiatedSignon.aspx
where <ADFS FQDN> is the FQDN of your ADFS server or VIP's DNS entry.

Monday, 30 June 2014

Configure Federation Trust for Claim Based authentication in Ms Dynamics CRM 2013 using ADFS 3.0 ( Windows Server 2012 R2) part 3

In part 1, I described how to install and configure ADFS on a Windows 2012 R2 server. In part 2, I described how to configure Ms Dynamics CRM 2013 to use claim based authentication and in this final post of this series I configure a federation trust to allow users from a separate forest to access MS Dynamics CRM 2013.
 
In our case, this is due to an acquisition, so that we are hosting Dynamics CRM in a domain called dev.local and the company bought has their own domain called taleb.local.
 
The objective is to allow users from taleb.local to log on, with their taleb.local accounts into our instance of Dynamics CRM, so we could say that taleb.local is the Accounts domain and dev.local is the Dynamics Domain.
 
Pre-Requisites:
  • Working ADFS servers on both domain/forests
  • Name resolution working between domain/forests as well as inside forests, I have used stub zones but any way that works for you is good.
  • TCP port 443 between ADFS servers and Dynamics Servers(or at least Front End Servers) open.
  • Account with permissions to configure both ADFS servers.
 
In our case, due to the internal use, Public certificates are not in use, so the first step is to ensure that taleb.local machines trust the dev.local CA. This is out of scope for this post, see this link for details.
 
The ADFS server(s) in dev.local need to trust the taleb.local CA, which can be achieved by adding the certificate to the trusted store for the computer account.
 
On the Accounts domain's (taleb.local) ADFS server, add a Relying Party Trust for the dev.local ADFS endpoint:
 







When the Claim Rules window opens, Click add Rule and Add a Send LDAP Attribute as Claims Claim Rule. The only LDAP Attribute that we are after is UPN.






On the Dynamics domain's (dev.local) ADFS Server, Add a Claims Provider Trust for the Accounts domain (taleb.local) ADFS endpoint:








When the Claim Rules window opens, Click add Rule and Add a Pass Through or Filter an Incoming Claim Claim Rule. I think that we only really need UPN, but I've added Windows Account Name and Primary SID as well for good measure.





 
 
At this point, all that remains is for accounts from the Accounts domain (taleb.local) to be added to Dynamics CRM as users and given a role so that they can log in to Dynamics CRM.
 
Ensure that UPN is used to add a new user to Dynamics CRM, e.g. For a user with logon: taleb\NN, the username in Dynamics CRM would be: nn@taleb.local. Annoyingly, it doesn't retrieve first name, last name so these will need to be added manually. I'd love to know if it's actually possible (to get the first/last name to auto populate) by adding more claims but I've never been able to get it working.
 
I've added both domains to the trusted sites as our default configuration is for automatic logon with current username and password on trusted sites.
 

Navigating to https://allinone.dev.local/lospolloshermanos/main.aspx from a machine in the accounts domain (taleb.local) brings this screen up: 


Selecting sts1.taleb.local will redirect to https://allinone.dev.local/lospolloshermanos/main.aspx with the current account from the accounts domain (taleb.local).



 

Monday, 23 June 2014

Use Linq Joins (Method Syntax) with Late Binding in Ms Dynamics CRM 2011/2013

There seems to be no examples of using Linq with late binding and method syntax about, until now:

var collection = context.CreateQuery("service")
.Join(context.CreateQuery("e2_dictionary"),x => x["serviceid"], y => (y["e2_serviceid"as EntityReference).Id, (x, y) => y)
.Where(x => x["name"].Equals(serviceName));
The out of the box service entity is pretty locked down, it's not possible to add N:1 or N:N relationships, so in order to implement a classification system for the services, I created the e2_dictionary entity.

e2_dictionary has a N:1 with e2_type and service, which means that there is some validation needed in a plug-in to ensure that when that nobody creates more than one e2_dictionary and this is why the snippet above, serviceName is the name of the service :)
 
 


Monday, 16 June 2014

Install/Uninstall library/assembly (DLL) in to GAC using PowerShell

On Friday, I screwed a little bit, as the build relied on some assemblies being in the GAC and I had not added them to the GAC.
 
I normally use Gacutil but this being a test server, it had no such tools, but no matter, PowerShell to the rescue.
Add-Type -AssemblyName System.EnterpriseServices
$Publisher = New-Object System.EnterpriseServices.Internal.Publish
$Publisher.GacInstall("C:\Users\Account\Desktop\mydll.dll)
gwmi win32_service | ? {$_.Name -match "MSCRM|W3SVC"} | %{Restart-Service -Name $_.Name}

The last step restarts all services containing MSCRM on its name as well as the World Wide Publishing Service, so that both async and sync plugins can make use of this library.

Removing the Assembly from the Gac can be achieved with the following commands:
Add-Type -AssemblyName System.EnterpriseServices
$Publisher = New-Object System.EnterpriseServices.Internal.Publish
$Publisher.GacRemove("C:\Users\Account\Desktop\mydll.dll)
gwmi win32_service | ? {$_.Name -match "MSCRM|W3SVC"} | %{Restart-Service -Name $_.Name}
Add-Type is a PowerShell 3.0 onwards cmdlet, you can use this instead:
[System.Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices")