Implement Create/Delete/List person users functionality in ssoAdminClient

This commit is contained in:
dmilov
2020-09-28 17:59:13 +03:00
parent 64e0b52224
commit 391660cdf4
23 changed files with 20433 additions and 21 deletions

View File

@@ -0,0 +1,5 @@
<configuration>
<packageSources>
<add key="LocalPackages" value="packages" />
</packageSources>
</configuration>

View File

@@ -1,5 +1,5 @@
// **************************************************************************
// Copyright 2019 VMware, Inc. All rights reserved. -- VMware Confidential.
// Copyright (c) VMware, Inc. All rights reserved. -- VMware Confidential.
// **************************************************************************
using System;
using System.Collections;
@@ -96,6 +96,13 @@ namespace VMware.vSphere.LsClient
return FindServiceEndpoint(product, type, endpointType);
}
public Uri GetStsEndpointUri() {
var product = "com.vmware.cis";
var type = "cs.identity";
var endpointType = "com.vmware.cis.cs.identity.sso";
return FindServiceEndpoint(product, type, endpointType);
}
private Uri FindServiceEndpoint(string product, string type, string endpointType) {
Uri result = null;

View File

@@ -9,15 +9,16 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.IdentityModel" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.ServiceModel.Primitives" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Duplex" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Http" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.NetTcp" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Security" Version="4.4.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<PackageReference Include="VMware.System.Private.ServiceModel" Version="4.4.4" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services" />

View File

@@ -3,11 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30503.244
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMware.vSphere.SsoAdminClient", "VMware.vSphere.SsoAdminClient\VMware.vSphere.SsoAdminClient.csproj", "{BD48E0DD-4048-48FD-B0BE-560E2417A2CC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VMware.vSphere.SsoAdminClient", "VMware.vSphere.SsoAdminClient\VMware.vSphere.SsoAdminClient.csproj", "{BD48E0DD-4048-48FD-B0BE-560E2417A2CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMware.vSphere.LsClient", "VMware.vSphere.LsClient\VMware.vSphere.LsClient.csproj", "{EEC4C335-3E6C-4FA5-84CD-CBADCD720F35}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VMware.vSphere.LsClient", "VMware.vSphere.LsClient\VMware.vSphere.LsClient.csproj", "{EEC4C335-3E6C-4FA5-84CD-CBADCD720F35}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMware.vSphere.SsoAdmin.Utils", "VMware.vSphere.SsoAdmin.Utils\VMware.vSphere.SsoAdmin.Utils.csproj", "{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VMware.vSphere.SsoAdmin.Utils", "VMware.vSphere.SsoAdmin.Utils\VMware.vSphere.SsoAdmin.Utils.csproj", "{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VMware.vSphere.SsoAdminClient.Tests", "VMware.vSphere.SsoAdminClient.Tests\VMware.vSphere.SsoAdminClient.Tests.csproj", "{90E6C4A6-FDB4-43FC-B156-ADBCF2B85CCE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -23,10 +25,14 @@ Global
{EEC4C335-3E6C-4FA5-84CD-CBADCD720F35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEC4C335-3E6C-4FA5-84CD-CBADCD720F35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEC4C335-3E6C-4FA5-84CD-CBADCD720F35}.Release|Any CPU.Build.0 = Release|Any CPU
{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1523743E-C01E-4D37-845F-0BB8DAF9EE7E}.Release|Any CPU.Build.0 = Release|Any CPU
{90E6C4A6-FDB4-43FC-B156-ADBCF2B85CCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90E6C4A6-FDB4-43FC-B156-ADBCF2B85CCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90E6C4A6-FDB4-43FC-B156-ADBCF2B85CCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90E6C4A6-FDB4-43FC-B156-ADBCF2B85CCE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -9,14 +9,15 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.IdentityModel" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.ServiceModel.Primitives" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Duplex" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Http" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.NetTcp" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Security" Version="4.4.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<PackageReference Include="VMware.System.Private.ServiceModel" Version="4.4.4" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,53 @@
using NUnit.Framework;
using System.Security;
using VMware.vSphere.SsoAdmin.Utils;
namespace VMware.vSphere.SsoAdminClient.Tests
{
public class Tests
{
private string _vc = "<place VC address here>";
private string _user = "<place VC user here>";
private string _rawPassword = "<place password here>";
private SecureString _password;
[SetUp]
public void Setup() {
_password = new SecureString();
foreach (char c in _rawPassword) {
_password.AppendChar(c);
}
}
[Test]
public void AddRemoveLocalUser() {
// Arrange
var ssoAdminClient = new SsoAdminClient(_vc, _user, _password, new AcceptAllX509CertificateValidator());
var expectedUserName = "test-user2";
var expectedPassword = "te$tPa$sW0rd";
var expectedDescription = "test-description";
var expectedEmail = "testuse@testdomain.loc";
var expectedFirstName = "Test";
var expectedLastName = "User";
// Act Create User
var actual = ssoAdminClient.CreateLocalUser(
expectedUserName,
expectedPassword,
expectedDescription,
expectedEmail,
expectedFirstName,
expectedLastName);
// Assert Created User
Assert.AreEqual(expectedUserName, actual.Name);
Assert.AreEqual(expectedDescription, actual.Description);
Assert.AreEqual(expectedEmail, actual.EmailAddress);
Assert.AreEqual(expectedFirstName, actual.FirstName);
Assert.AreEqual(expectedLastName, actual.LastName);
// Act Delete User
ssoAdminClient.DeleteLocalUser(
actual);
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VMware.vSphere.SsoAdmin.Utils\VMware.vSphere.SsoAdmin.Utils.csproj" />
<ProjectReference Include="..\VMware.vSphere.SsoAdminClient\VMware.vSphere.SsoAdminClient.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,8 +0,0 @@
using System;
namespace VMware.vSphere.SsoAdminClient
{
public class Class1
{
}
}

View File

@@ -0,0 +1,25 @@
// **************************************************************************
// Copyright (c) VMware, Inc. All rights reserved. -- VMware Confidential.
// **************************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VMware.vSphere.SsoAdminClient.DataTypes
{
public class Principal
{
public string Name { get; set; }
public string Domain { get; set; }
public string Description { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public override string ToString() {
return $"{Name}@{Domain}";
}
}
}

View File

@@ -0,0 +1,227 @@
// **************************************************************************
// Copyright (c) VMware, Inc. All rights reserved. -- VMware Confidential.
// **************************************************************************
using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.Security;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.Text;
using VMware.Binding.WsTrust;
using VMware.Binding.WsTrust.SecurityContext;
using VMware.vSphere.LsClient;
using VMware.vSphere.SsoAdminClient.DataTypes;
using VMware.vSphere.SsoAdminClient.SsoAdminServiceReferencer;
namespace VMware.vSphere.SsoAdminClient
{
public class SsoAdminClient
{
private const int WEB_OPERATION_TIMEOUT_SECONDS = 30;
private string _server;
private SsoPortTypeClient _ssoAdminBindingClient;
private UserPassSecurityContext _securityContext;
public SsoAdminClient(string hostname, string user, SecureString password, X509CertificateValidator serverCertificateValidator) {
if (hostname == null) throw new ArgumentNullException(nameof(hostname));
if (user == null) throw new ArgumentNullException(nameof(user));
if (password == null) throw new ArgumentNullException(nameof(password));
_server = hostname;
var lsClient = new LookupServiceClient(hostname, serverCertificateValidator);
// Create STS Client
var stsUri = lsClient.GetStsEndpointUri();
_securityContext = new UserPassSecurityContext(user, password, stsUri, serverCertificateValidator);
// Create SSO Admin Binding Client
var ssoAdminUri = lsClient.GetSsoAdminEndpointUri();
_ssoAdminBindingClient = new SsoPortTypeClient(GetBinding(), new EndpointAddress(ssoAdminUri));
_ssoAdminBindingClient.ChannelFactory.Endpoint.EndpointBehaviors.Add(new WsTrustBehavior());
var serverAuthentication = GetServerAuthentication(serverCertificateValidator);
if (serverAuthentication != null) {
_ssoAdminBindingClient
.ChannelFactory
.Credentials
.ServiceCertificate
.SslCertificateAuthentication = serverAuthentication;
}
}
#region Private Helpers
private X509ServiceCertificateAuthentication GetServerAuthentication(X509CertificateValidator serverCertificateValidator) {
if (serverCertificateValidator != null) {
return new X509ServiceCertificateAuthentication {
CertificateValidationMode = X509CertificateValidationMode.Custom,
CustomCertificateValidator = serverCertificateValidator
};
}
// Default .NET behavior for TLS certificate validation
return null;
}
private static MessageEncodingBindingElement GetWcfEncoding() {
// VMware STS requires SOAP version 1.1
return new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
}
private static HttpsTransportBindingElement GetWcfTransport(bool useSystemProxy) {
// Communication with the STS is over https
HttpsTransportBindingElement transport = new HttpsTransportBindingElement {
RequireClientCertificate = false
};
transport.UseDefaultWebProxy = useSystemProxy;
transport.MaxBufferSize = 2147483647;
transport.MaxReceivedMessageSize = 2147483647;
return transport;
}
private static CustomBinding GetBinding() {
// There is no build-in WCF binding capable of communicating
// with VMware STS, so we create a plain custom one.
// This binding does not provide support for WS-Trust,
// that support is currently implemented as a WCF endpoint behaviour.
var binding = new CustomBinding(GetWcfEncoding(), GetWcfTransport(true));
var timeout = TimeSpan.FromSeconds(WEB_OPERATION_TIMEOUT_SECONDS);
binding.CloseTimeout = timeout;
binding.OpenTimeout = timeout;
binding.ReceiveTimeout = timeout;
binding.SendTimeout = timeout;
return binding;
}
private WsSecurityContext CreateAuthorizedInvocationContext() {
// Issue Bearer token to authorize create solution user to SSO Admin service
var bearerToken = _securityContext.GetToken();
// Set WS Trust Header Serialization with issued bearer SAML token
var securityContext = new WsSecurityContext {
ClientChannel = _ssoAdminBindingClient.InnerChannel,
Properties = {
Credentials = {
BearerToken = bearerToken
}
}
};
return securityContext;
}
#endregion
#region Public interface
public Principal CreateLocalUser(
string userName,
string password,
string description = null,
string emailAddress = null,
string firstName = null,
string lastName = null) {
// Create Authorization Invocation Context
var authorizedInvocationContext =
CreateAuthorizedInvocationContext();
// Invoke SSO Admin CreateLocalSolutionUser operation
var ssoPrincipalId = authorizedInvocationContext.
InvokeOperation(() =>
_ssoAdminBindingClient.CreateLocalPersonUserAsync(
new ManagedObjectReference {
type = "SsoAdminPrincipalManagementService",
Value = "principalManagementService"
},
userName,
new SsoAdminPersonDetails {
description = description,
emailAddress = emailAddress,
firstName = firstName,
lastName = lastName
},
password)).Result;
return GetLocalUsers(ssoPrincipalId.name, ssoPrincipalId.domain, authorizedInvocationContext);
}
private Principal GetLocalUsers(string userName, string domain, WsSecurityContext wsSecurityContext) {
// Invoke SSO Admin FindPersonUserAsync operation
var personUser = wsSecurityContext.
InvokeOperation(() =>
_ssoAdminBindingClient.FindPersonUserAsync(
new ManagedObjectReference {
type = "SsoAdminPrincipalDiscoveryService",
Value = "principalDiscoveryService"
},
new SsoPrincipalId {
name = userName,
domain = domain
})).Result;
return new Principal {
Name = personUser.id.name,
Domain = personUser.id.domain,
Description = personUser.details.description,
FirstName = personUser.details.firstName,
LastName = personUser.details.lastName,
EmailAddress = personUser.details.emailAddress
};
}
public IEnumerable<Principal> GetAllLocalUsers() {
// Create Authorization Invocation Context
var authorizedInvocationContext =
CreateAuthorizedInvocationContext();
// Invoke SSO Admin FindPersonUsersAsync operation
var personUsers = authorizedInvocationContext.
InvokeOperation(() =>
_ssoAdminBindingClient.FindPersonUsersAsync(
new ManagedObjectReference {
type = "SsoAdminPrincipalDiscoveryService",
Value = "principalDiscoveryService"
},
new SsoAdminPrincipalDiscoveryServiceSearchCriteria (),
int.MaxValue)).Result.returnval;
foreach (var personUser in personUsers) {
yield return new Principal {
Name = personUser.id.name,
Domain = personUser.id.domain,
Description = personUser.details.description,
FirstName = personUser.details.firstName,
LastName = personUser.details.lastName,
EmailAddress = personUser.details.emailAddress
};
}
}
public void DeleteLocalUser(
Principal principal) {
// Create Authorization Invocation Context
var authorizedInvocationContext =
CreateAuthorizedInvocationContext();
// Invoke SSO Admin DeleteLocalPrincipal operation
authorizedInvocationContext.
InvokeOperation(() =>
_ssoAdminBindingClient.DeleteLocalPrincipalAsync(
new ManagedObjectReference {
type = "SsoAdminPrincipalManagementService",
Value = "principalManagementService"
},
principal.Name));
}
#endregion
}
}

View File

@@ -0,0 +1,48 @@
// **************************************************************************
// Copyright (c) VMware, Inc. All rights reserved. -- VMware Confidential.
// **************************************************************************
using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.Linq;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using VMware.Binding.Sts;
namespace VMware.vSphere.SsoAdminClient
{
public class UserPassSecurityContext
{
private string _user;
private SecureString _password;
private VmwareSecruityTokenService _stsClient;
public UserPassSecurityContext(
string user,
SecureString password,
Uri stsUri,
X509CertificateValidator serverCertificateValidator) {
if (user == null) throw new ArgumentNullException(nameof(user));
if (password == null) throw new ArgumentNullException(nameof(password));
if (stsUri == null) throw new ArgumentNullException(nameof(stsUri));
_user = user;
_password = password;
Action<X509Certificate2> certHandler = null;
if (serverCertificateValidator != null) {
certHandler = serverCertificateValidator.Validate;
}
_stsClient = new VmwareSecruityTokenService(stsUri, false, certHandler);
}
public XmlElement GetToken() {
return _stsClient.IssueBearerTokenByUserCredential(
_user,
_password).RawToken;
}
}
}

View File

@@ -6,5 +6,35 @@
<Description>SSO Admin API client.</Description>
<TargetFrameworks>net45;netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net45' ">
<DefineConstants>$(DefineConstants);NET45</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
<DefineConstants>$(DefineConstants);NETCORE20</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.IdentityModel" />
<PackageReference Include="System.ServiceModel.Primitives" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Duplex" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Http" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.NetTcp" Version="4.4.0" />
<PackageReference Include="System.ServiceModel.Security" Version="4.4.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<PackageReference Include="VMware.System.Private.ServiceModel" Version="4.4.4" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="VMware.Binding.Sts" Version="12.0.0.15939652" />
<PackageReference Include="VMware.Binding.WsTrust" Version="12.0.0.15939652" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VMware.vSphere.LsClient\VMware.vSphere.LsClient.csproj" />
</ItemGroup>
</Project>

View File

@@ -23,7 +23,7 @@ Import-Module $modulePath
$script:lsClient = $null
Describe "Lookup Service Client Integration Tests" {
Context "Retrieval of SsoAdmin API Url" {
Context "Retrieval of Service API Url" {
BeforeAll {
## Create LsClient
$skipCertificateCheckValidator = New-Object `
@@ -42,6 +42,15 @@ Describe "Lookup Service Client Integration Tests" {
# Assert
$actual | Should Not Be $null
$actual.ToString().StartsWith("https://$VCAddress/sso-adminserver/sdk/") | Should Be $true
}
}
It 'Gets STS API Url' {
# Act
$actual = $script:lsClient.GetStsEndpointUri()
# Assert
$actual | Should Not Be $null
$actual.ToString().StartsWith("https://$VCAddress/sts/STSService") | Should Be $true
}
}
}