Imported Upstream version 4.6.2
This commit is contained in:
26
ipatests/test_ipaserver/__init__.py
Normal file
26
ipatests/test_ipaserver/__init__.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Authors:
|
||||
# Jason Gerard DeRose <jderose@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2008 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Sub-package containing unit tests for `ipaserver` package.
|
||||
"""
|
||||
import ipatests.util
|
||||
|
||||
|
||||
ipatests.util.check_ipaclient_unittests()
|
||||
48
ipatests/test_ipaserver/data/full.xml
Normal file
48
ipatests/test_ipaserver/data/full.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0"?>
|
||||
<KeyContainer xmlns="urn:ietf:params:xml:ns:keyprov:pskc" Version="1.0" Id="KCID">
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>iana.dummy</Manufacturer>
|
||||
<SerialNo>SerialNo</SerialNo>
|
||||
<Model>Model</Model>
|
||||
<IssueNo>IssueNo</IssueNo>
|
||||
<DeviceBinding>DeviceBinding</DeviceBinding>
|
||||
<StartDate>2006-05-01T00:00:00Z</StartDate>
|
||||
<ExpiryDate>2012-05-01T00:00:00Z</ExpiryDate>
|
||||
<UserId>DeviceUserId</UserId>
|
||||
</DeviceInfo>
|
||||
<CryptoModuleInfo>
|
||||
<Id>CMID</Id>
|
||||
</CryptoModuleInfo>
|
||||
<Key Id="KID1" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
|
||||
<Issuer>Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<Suite>Suite</Suite>
|
||||
<ChallengeFormat Encoding="DECIMAL" Min="42" Max="4711" CheckDigits="true"/>
|
||||
<ResponseFormat Encoding="DECIMAL" Length="8" CheckDigits="true"/>
|
||||
</AlgorithmParameters>
|
||||
<KeyProfileId>KeyProfileId</KeyProfileId>
|
||||
<KeyReference>KeyReference</KeyReference>
|
||||
<FriendlyName>FriendlyName</FriendlyName>
|
||||
<Data>
|
||||
<Secret>
|
||||
<PlainValue>MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=</PlainValue>
|
||||
</Secret>
|
||||
<Counter>
|
||||
<PlainValue>0</PlainValue>
|
||||
</Counter>
|
||||
<TimeInterval>
|
||||
<PlainValue>200</PlainValue>
|
||||
</TimeInterval>
|
||||
<TimeDrift>
|
||||
<PlainValue>300</PlainValue>
|
||||
</TimeDrift>
|
||||
</Data>
|
||||
<UserId>KeyUserId</UserId>
|
||||
<Policy>
|
||||
<StartDate>2006-05-01T00:00:00Z</StartDate>
|
||||
<ExpiryDate>2006-05-31T00:00:00Z</ExpiryDate>
|
||||
</Policy>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
</KeyContainer>
|
||||
32
ipatests/test_ipaserver/data/pskc-figure3.xml
Normal file
32
ipatests/test_ipaserver/data/pskc-figure3.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<KeyContainer Version="1.0"
|
||||
Id="exampleID1"
|
||||
xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>Manufacturer</Manufacturer>
|
||||
<SerialNo>987654321</SerialNo>
|
||||
<UserId>DC=example-bank,DC=net</UserId>
|
||||
</DeviceInfo>
|
||||
<CryptoModuleInfo>
|
||||
<Id>CM_ID_001</Id>
|
||||
</CryptoModuleInfo>
|
||||
<Key Id="12345678"
|
||||
Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
|
||||
<Issuer>Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<ResponseFormat Length="8" Encoding="DECIMAL"/>
|
||||
</AlgorithmParameters>
|
||||
<Data>
|
||||
<Secret>
|
||||
<PlainValue>MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
|
||||
</PlainValue>
|
||||
</Secret>
|
||||
<Counter>
|
||||
<PlainValue>0</PlainValue>
|
||||
</Counter>
|
||||
</Data>
|
||||
<UserId>UID=jsmith,DC=example-bank,DC=net</UserId>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
</KeyContainer>
|
||||
31
ipatests/test_ipaserver/data/pskc-figure4.xml
Normal file
31
ipatests/test_ipaserver/data/pskc-figure4.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<KeyContainer Version="1.0" Id="exampleID1"
|
||||
xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>Manufacturer</Manufacturer>
|
||||
<SerialNo>987654321</SerialNo>
|
||||
</DeviceInfo>
|
||||
<CryptoModuleInfo>
|
||||
<Id>CM_ID_001</Id>
|
||||
</CryptoModuleInfo>
|
||||
<Key Id="12345678"
|
||||
Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
|
||||
<Issuer>Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<ResponseFormat Length="8" Encoding="DECIMAL"/>
|
||||
</AlgorithmParameters>
|
||||
<KeyProfileId>keyProfile1</KeyProfileId>
|
||||
<KeyReference>MasterKeyLabel
|
||||
</KeyReference>
|
||||
<Data>
|
||||
<Counter>
|
||||
<PlainValue>0</PlainValue>
|
||||
</Counter>
|
||||
</Data>
|
||||
<Policy>
|
||||
<KeyUsage>OTP</KeyUsage>
|
||||
</Policy>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
</KeyContainer>
|
||||
57
ipatests/test_ipaserver/data/pskc-figure5.xml
Normal file
57
ipatests/test_ipaserver/data/pskc-figure5.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<KeyContainer
|
||||
Version="1.0" Id="exampleID1"
|
||||
xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>Manufacturer</Manufacturer>
|
||||
<SerialNo>987654321</SerialNo>
|
||||
</DeviceInfo>
|
||||
<CryptoModuleInfo>
|
||||
<Id>CM_ID_001</Id>
|
||||
</CryptoModuleInfo>
|
||||
<Key Id="12345678"
|
||||
Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
|
||||
<Issuer>Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<ResponseFormat Length="8" Encoding="DECIMAL"/>
|
||||
</AlgorithmParameters>
|
||||
<Data>
|
||||
<Secret>
|
||||
<PlainValue>MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
|
||||
</PlainValue>
|
||||
</Secret>
|
||||
<Counter>
|
||||
<PlainValue>0</PlainValue>
|
||||
</Counter>
|
||||
</Data>
|
||||
<Policy>
|
||||
<PINPolicy MinLength="4" MaxLength="4"
|
||||
PINKeyId="123456781" PINEncoding="DECIMAL"
|
||||
PINUsageMode="Local"/>
|
||||
<KeyUsage>OTP</KeyUsage>
|
||||
</Policy>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>Manufacturer</Manufacturer>
|
||||
<SerialNo>987654321</SerialNo>
|
||||
</DeviceInfo>
|
||||
<CryptoModuleInfo>
|
||||
<Id>CM_ID_001</Id>
|
||||
</CryptoModuleInfo>
|
||||
<Key Id="123456781"
|
||||
Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:pin">
|
||||
<Issuer>Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<ResponseFormat Length="4" Encoding="DECIMAL"/>
|
||||
</AlgorithmParameters>
|
||||
<Data>
|
||||
<Secret>
|
||||
<PlainValue>MTIzNA==</PlainValue>
|
||||
</Secret>
|
||||
</Data>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
</KeyContainer>
|
||||
47
ipatests/test_ipaserver/data/pskc-figure6.xml
Normal file
47
ipatests/test_ipaserver/data/pskc-figure6.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<KeyContainer Version="1.0"
|
||||
xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||
<EncryptionKey>
|
||||
<ds:KeyName>Pre-shared-key</ds:KeyName>
|
||||
</EncryptionKey>
|
||||
<MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
|
||||
<MACKey>
|
||||
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
|
||||
<xenc:CipherData>
|
||||
<xenc:CipherValue>ESIzRFVmd4iZABEiM0RVZgKn6WjLaTC1sbeBMSvIhRejN9vJa2BOlSaMrR7I5wSX</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
</MACKey>
|
||||
</MACMethod>
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>Manufacturer</Manufacturer>
|
||||
<SerialNo>987654321</SerialNo>
|
||||
</DeviceInfo>
|
||||
<CryptoModuleInfo>
|
||||
<Id>CM_ID_001</Id>
|
||||
</CryptoModuleInfo>
|
||||
<Key Id="12345678"
|
||||
Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
|
||||
<Issuer>Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<ResponseFormat Length="8" Encoding="DECIMAL"/>
|
||||
</AlgorithmParameters>
|
||||
<Data>
|
||||
<Secret>
|
||||
<EncryptedValue>
|
||||
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
|
||||
<xenc:CipherData>
|
||||
<xenc:CipherValue>AAECAwQFBgcICQoLDA0OD+cIHItlB3Wra1DUpxVvOx2lef1VmNPCMl8jwZqIUqGv</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
</EncryptedValue>
|
||||
<ValueMAC>Su+NvtQfmvfJzF6bmQiJqoLRExc=</ValueMAC>
|
||||
</Secret>
|
||||
<Counter>
|
||||
<PlainValue>0</PlainValue>
|
||||
</Counter>
|
||||
</Data>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
</KeyContainer>
|
||||
68
ipatests/test_ipaserver/data/pskc-figure7.xml
Normal file
68
ipatests/test_ipaserver/data/pskc-figure7.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pskc:KeyContainer
|
||||
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc"
|
||||
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#"
|
||||
xmlns:pkcs5="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#"
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Version="1.0">
|
||||
<pskc:EncryptionKey>
|
||||
<xenc11:DerivedKey>
|
||||
<xenc11:KeyDerivationMethod
|
||||
Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2">
|
||||
<pkcs5:PBKDF2-params>
|
||||
<Salt>
|
||||
<Specified>Ej7/PEpyEpw=</Specified>
|
||||
</Salt>
|
||||
<IterationCount>1000</IterationCount>
|
||||
<KeyLength>16</KeyLength>
|
||||
<PRF/>
|
||||
</pkcs5:PBKDF2-params>
|
||||
</xenc11:KeyDerivationMethod>
|
||||
<xenc:ReferenceList>
|
||||
<xenc:DataReference URI="#ED"/>
|
||||
</xenc:ReferenceList>
|
||||
<xenc11:MasterKeyName>My Password 1</xenc11:MasterKeyName>
|
||||
</xenc11:DerivedKey>
|
||||
</pskc:EncryptionKey>
|
||||
<pskc:MACMethod
|
||||
Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
|
||||
<pskc:MACKey>
|
||||
<xenc:EncryptionMethod
|
||||
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
|
||||
<xenc:CipherData>
|
||||
<xenc:CipherValue>
|
||||
2GTTnLwM3I4e5IO5FkufoOEiOhNj91fhKRQBtBJYluUDsPOLTfUvoU2dStyOwYZx
|
||||
</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
</pskc:MACKey>
|
||||
</pskc:MACMethod>
|
||||
<pskc:KeyPackage>
|
||||
<pskc:DeviceInfo>
|
||||
<pskc:Manufacturer>TokenVendorAcme</pskc:Manufacturer>
|
||||
<pskc:SerialNo>987654321</pskc:SerialNo>
|
||||
</pskc:DeviceInfo>
|
||||
<pskc:CryptoModuleInfo>
|
||||
<pskc:Id>CM_ID_001</pskc:Id>
|
||||
</pskc:CryptoModuleInfo>
|
||||
<pskc:Key Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp" Id="123456">
|
||||
<pskc:Issuer>Example-Issuer</pskc:Issuer>
|
||||
<pskc:AlgorithmParameters>
|
||||
<pskc:ResponseFormat Length="8" Encoding="DECIMAL"/>
|
||||
</pskc:AlgorithmParameters>
|
||||
<pskc:Data>
|
||||
<pskc:Secret>
|
||||
<pskc:EncryptedValue Id="ED">
|
||||
<xenc:EncryptionMethod
|
||||
Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
|
||||
<xenc:CipherData>
|
||||
<xenc:CipherValue>
|
||||
oTvo+S22nsmS2Z/RtcoF8Hfh+jzMe0RkiafpoDpnoZTjPYZu6V+A4aEn032yCr4f
|
||||
</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
</pskc:EncryptedValue>
|
||||
<pskc:ValueMAC>LP6xMvjtypbfT9PdkJhBZ+D6O4w=
|
||||
</pskc:ValueMAC>
|
||||
</pskc:Secret>
|
||||
</pskc:Data>
|
||||
</pskc:Key>
|
||||
</pskc:KeyPackage>
|
||||
</pskc:KeyContainer>
|
||||
53
ipatests/test_ipaserver/data/pskc-figure8.xml
Normal file
53
ipatests/test_ipaserver/data/pskc-figure8.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<KeyContainer
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||
xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
|
||||
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
|
||||
Id="KC0001"
|
||||
Version="1.0">
|
||||
<EncryptionKey>
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>MIIB5zCCAVCgAwIBAgIESZp/vDANBgkqhkiG9w0BAQUFADA4M
|
||||
Q0wCwYDVQQKEwRJRVRGMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIF
|
||||
Rlc3QwHhcNMDkwMjE3MDkxMzMyWhcNMTEwMjE3MDkxMzMyWjA4MQ0wCwYDVQQKEwRJRVR
|
||||
GMRMwEQYDVQQLEwpLZXlQcm92IFdHMRIwEAYDVQQDEwlQU0tDIFRlc3QwgZ8wDQYJKoZI
|
||||
hvcNAQEBBQADgY0AMIGJAoGBALCWLDa2ItYJ6su80hd1gL4cggQYdyyKK17btt/aS6Q/e
|
||||
DsKjsPyFIODsxeKVV/uA3wLT4jQJM5euKJXkDajzGGOy92+ypfzTX4zDJMkh61SZwlHNJ
|
||||
xBKilAM5aW7C+BQ0RvCxvdYtzx2LTdB+X/KMEBA7uIYxLfXH2Mnub3WIh1AgMBAAEwDQY
|
||||
JKoZIhvcNAQEFBQADgYEAe875m84sYUJ8qPeZ+NG7REgTvlHTmoCdoByU0LBBLotUKuqf
|
||||
rnRuXJRMeZXaaEGmzY1kLonVjQGzjAkU4dJ+RPmiDlYuHLZS41Pg6VMwY+03lhk6I5A/w
|
||||
4rnqdkmwZX/NgXg06alnc2pBsXWhL4O7nk0S2ZrLMsQZ6HcsXgdmHo=
|
||||
</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</EncryptionKey>
|
||||
<KeyPackage>
|
||||
<DeviceInfo>
|
||||
<Manufacturer>TokenVendorAcme</Manufacturer>
|
||||
<SerialNo>987654321</SerialNo>
|
||||
</DeviceInfo>
|
||||
<Key Id="MBK000000001"
|
||||
Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
|
||||
<Issuer>Example-Issuer</Issuer>
|
||||
<AlgorithmParameters>
|
||||
<ResponseFormat Length="6" Encoding="DECIMAL"/>
|
||||
</AlgorithmParameters>
|
||||
<Data>
|
||||
<Secret>
|
||||
<EncryptedValue>
|
||||
<xenc:EncryptionMethod
|
||||
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa_1_5"/>
|
||||
<xenc:CipherData>
|
||||
<xenc:CipherValue>hJ+fvpoMPMO9BYpK2rdyQYGIxiATYHTHC7e/sPLKYo5/r1v+4
|
||||
xTYG3gJolCWuVMydJ7Ta0GaiBPHcWa8ctCVYmHKfSz5fdeV5nqbZApe6dofTqhRwZK6
|
||||
Yx4ufevi91cjN2vBpSxYafvN3c3+xIgk0EnTV4iVPRCR0rBwyfFrPc4=
|
||||
</xenc:CipherValue>
|
||||
</xenc:CipherData>
|
||||
</EncryptedValue>
|
||||
</Secret>
|
||||
<Counter>
|
||||
<PlainValue>0</PlainValue>
|
||||
</Counter>
|
||||
</Data>
|
||||
</Key>
|
||||
</KeyPackage>
|
||||
</KeyContainer>
|
||||
3
ipatests/test_ipaserver/data/pskc-invalid.xml
Normal file
3
ipatests/test_ipaserver/data/pskc-invalid.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
<SomethingElse>
|
||||
</SomethingElse>
|
||||
4
ipatests/test_ipaserver/data/pskc-mini.xml
Normal file
4
ipatests/test_ipaserver/data/pskc-mini.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<KeyContainer xmlns="urn:ietf:params:xml:ns:keyprov:pskc" Version="1.0">
|
||||
<KeyPackage/>
|
||||
</KeyContainer>
|
||||
55
ipatests/test_ipaserver/httptest.py
Normal file
55
ipatests/test_ipaserver/httptest.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Authors:
|
||||
# Martin Kosek <mkosek@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2012 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
Base class for HTTP request tests
|
||||
"""
|
||||
|
||||
from six.moves import urllib
|
||||
|
||||
from ipalib import api, util
|
||||
|
||||
class Unauthorized_HTTP_test(object):
|
||||
"""
|
||||
Base class for simple HTTP request tests executed against URI
|
||||
with no required authorization
|
||||
"""
|
||||
app_uri = ''
|
||||
host = api.env.host
|
||||
cacert = api.env.tls_ca_cert
|
||||
content_type = 'application/x-www-form-urlencoded'
|
||||
|
||||
def send_request(self, method='POST', params=None):
|
||||
"""
|
||||
Send a request to HTTP server
|
||||
|
||||
:param key When not None, overrides default app_uri
|
||||
"""
|
||||
if params is not None:
|
||||
# urlencode *can* take two arguments
|
||||
# pylint: disable=too-many-function-args
|
||||
params = urllib.parse.urlencode(params, True)
|
||||
url = 'https://' + self.host + self.app_uri
|
||||
|
||||
headers = {'Content-Type' : self.content_type,
|
||||
'Referer' : url}
|
||||
|
||||
conn = util.create_https_connection(
|
||||
self.host, cafile=self.cacert)
|
||||
conn.request(method, self.app_uri, params, headers)
|
||||
return conn.getresponse()
|
||||
108
ipatests/test_ipaserver/test_changepw.py
Normal file
108
ipatests/test_ipaserver/test_changepw.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# Authors:
|
||||
# Martin Kosek <mkosek@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2012 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import nose
|
||||
import ldap
|
||||
import pytest
|
||||
|
||||
from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test
|
||||
from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
|
||||
from ipatests.util import assert_equal
|
||||
from ipalib import api, errors
|
||||
from ipapython.dn import DN
|
||||
|
||||
testuser = u'tuser'
|
||||
old_password = u'old_password'
|
||||
new_password = u'new_password'
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class test_changepw(XMLRPC_test, Unauthorized_HTTP_test):
|
||||
app_uri = '/ipa/session/change_password'
|
||||
|
||||
def setup(self):
|
||||
try:
|
||||
api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User')
|
||||
api.Command['passwd'](testuser, password=u'old_password')
|
||||
except errors.ExecutionError as e:
|
||||
raise nose.SkipTest(
|
||||
'Cannot set up test user: %s' % e
|
||||
)
|
||||
|
||||
def teardown(self):
|
||||
try:
|
||||
api.Command['user_del']([testuser])
|
||||
except errors.NotFound:
|
||||
pass
|
||||
|
||||
def _changepw(self, user, old_password, new_password):
|
||||
return self.send_request(params={'user': str(user),
|
||||
'old_password' : str(old_password),
|
||||
'new_password' : str(new_password)},
|
||||
)
|
||||
|
||||
def _checkpw(self, user, password):
|
||||
dn = str(DN(('uid', user), api.env.container_user, api.env.basedn))
|
||||
conn = ldap.initialize(api.env.ldap_uri)
|
||||
try:
|
||||
conn.simple_bind_s(dn, password)
|
||||
finally:
|
||||
conn.unbind_s()
|
||||
|
||||
def test_bad_options(self):
|
||||
for params in (None, # no params
|
||||
{'user': 'foo'}, # missing options
|
||||
{'user': 'foo',
|
||||
'old_password' : 'old'}, # missing option
|
||||
{'user': 'foo',
|
||||
'old_password' : 'old',
|
||||
'new_password' : ''}, # empty option
|
||||
):
|
||||
response = self.send_request(params=params)
|
||||
assert_equal(response.status, 400)
|
||||
assert_equal(response.reason, 'Bad Request')
|
||||
|
||||
def test_invalid_auth(self):
|
||||
response = self._changepw(testuser, 'wrongpassword', 'new_password')
|
||||
|
||||
assert_equal(response.status, 200)
|
||||
assert_equal(response.getheader('X-IPA-Pwchange-Result'), 'invalid-password')
|
||||
|
||||
# make sure that password is NOT changed
|
||||
self._checkpw(testuser, old_password)
|
||||
|
||||
def test_pwpolicy_error(self):
|
||||
response = self._changepw(testuser, old_password, '1')
|
||||
|
||||
assert_equal(response.status, 200)
|
||||
assert_equal(response.getheader('X-IPA-Pwchange-Result'), 'policy-error')
|
||||
assert_equal(response.getheader('X-IPA-Pwchange-Policy-Error'),
|
||||
'Constraint violation: Password is too short')
|
||||
|
||||
# make sure that password is NOT changed
|
||||
self._checkpw(testuser, old_password)
|
||||
|
||||
def test_pwpolicy_success(self):
|
||||
response = self._changepw(testuser, old_password, new_password)
|
||||
|
||||
assert_equal(response.status, 200)
|
||||
assert_equal(response.getheader('X-IPA-Pwchange-Result'), 'ok')
|
||||
|
||||
# make sure that password IS changed
|
||||
self._checkpw(testuser, new_password)
|
||||
41
ipatests/test_ipaserver/test_dnssec.py
Normal file
41
ipatests/test_ipaserver/test_dnssec.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
"""
|
||||
Test the `ipaserver/dnssec` package.
|
||||
"""
|
||||
import dns.name
|
||||
|
||||
from ipaserver.dnssec.odsmgr import ODSZoneListReader
|
||||
|
||||
|
||||
ZONELIST_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ZoneList>
|
||||
<Zone name="ipa.example">
|
||||
<Policy>default</Policy>
|
||||
<Adapters>
|
||||
<Input>
|
||||
<Adapter type="File">/var/lib/ipa/dns/zone/entryUUID/12345</Adapter>
|
||||
</Input>
|
||||
<Output>
|
||||
<Adapter type="File">/var/lib/ipa/dns/zone/entryUUID/12345</Adapter>
|
||||
</Output>
|
||||
</Adapters>
|
||||
</Zone>
|
||||
</ZoneList>
|
||||
"""
|
||||
|
||||
|
||||
def test_ods_zonelist_reader():
|
||||
uuid = '12345'
|
||||
name = dns.name.from_text('ipa.example.')
|
||||
|
||||
reader = ODSZoneListReader("<ZoneList/>")
|
||||
assert reader.mapping == {}
|
||||
assert reader.names == set()
|
||||
assert reader.uuids == set()
|
||||
|
||||
reader = ODSZoneListReader(ZONELIST_XML)
|
||||
assert reader.mapping == {uuid: name}
|
||||
assert reader.names == {name}
|
||||
assert reader.uuids == {uuid}
|
||||
0
ipatests/test_ipaserver/test_install/__init__.py
Normal file
0
ipatests/test_ipaserver/test_install/__init__.py
Normal file
63
ipatests/test_ipaserver/test_install/test_adtrustinstance.py
Executable file
63
ipatests/test_ipaserver/test_install/test_adtrustinstance.py
Executable file
@@ -0,0 +1,63 @@
|
||||
# Authors:
|
||||
# Sumit Bose <sbose@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2011 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
Test `adtrustinstance`
|
||||
"""
|
||||
import pytest
|
||||
import six
|
||||
|
||||
from ipaserver.install import adtrustinstance
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
@pytest.mark.tier0
|
||||
class test_adtrustinstance(object):
|
||||
"""
|
||||
Test `adtrustinstance`.
|
||||
"""
|
||||
|
||||
def test_make_netbios_name(self):
|
||||
s = adtrustinstance.make_netbios_name("ABCDEF")
|
||||
assert s == 'ABCDEF' and isinstance(s, str)
|
||||
s = adtrustinstance.make_netbios_name(U"ABCDEF")
|
||||
assert s == 'ABCDEF' and isinstance(s, unicode)
|
||||
s = adtrustinstance.make_netbios_name("abcdef")
|
||||
assert s == 'ABCDEF'
|
||||
s = adtrustinstance.make_netbios_name("abc.def")
|
||||
assert s == 'ABC'
|
||||
s = adtrustinstance.make_netbios_name("abcdefghijklmnopqr.def")
|
||||
assert s == 'ABCDEFGHIJKLMNO'
|
||||
s = adtrustinstance.make_netbios_name("A!$%B&/()C=?+*D")
|
||||
assert s == 'ABCD'
|
||||
s = adtrustinstance.make_netbios_name("!$%&/()=?+*")
|
||||
assert not s
|
||||
|
||||
def test_check_netbios_name(self):
|
||||
assert adtrustinstance.check_netbios_name("ABCDEF")
|
||||
assert not adtrustinstance.check_netbios_name("abcdef")
|
||||
assert adtrustinstance.check_netbios_name("ABCDE12345ABCDE")
|
||||
assert not adtrustinstance.check_netbios_name("ABCDE12345ABCDE1")
|
||||
assert not adtrustinstance.check_netbios_name("")
|
||||
|
||||
assert adtrustinstance.check_netbios_name(U"ABCDEF")
|
||||
assert not adtrustinstance.check_netbios_name(U"abcdef")
|
||||
assert adtrustinstance.check_netbios_name(U"ABCDE12345ABCDE")
|
||||
assert not adtrustinstance.check_netbios_name(U"ABCDE12345ABCDE1")
|
||||
125
ipatests/test_ipaserver/test_install/test_cainstance.py
Normal file
125
ipatests/test_ipaserver/test_install/test_cainstance.py
Normal file
@@ -0,0 +1,125 @@
|
||||
#
|
||||
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from binascii import hexlify
|
||||
import pickle
|
||||
# pylint: disable=import-error
|
||||
from six.moves.configparser import RawConfigParser
|
||||
# pylint: enable=import-error
|
||||
from six import StringIO
|
||||
import pytest
|
||||
from ipaserver.install import cainstance
|
||||
|
||||
pytestmark = pytest.mark.tier0
|
||||
|
||||
|
||||
class test_ExternalCAProfile(object):
|
||||
def test_MSCSTemplateV1_good(self):
|
||||
o = cainstance.MSCSTemplateV1("MySubCA")
|
||||
assert hexlify(o.get_ext_data()) == b'1e0e004d007900530075006200430041'
|
||||
|
||||
def test_MSCSTemplateV1_bad(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV1("MySubCA:1")
|
||||
|
||||
def test_MSCSTemplateV1_pickle_roundtrip(self):
|
||||
o = cainstance.MSCSTemplateV1("MySubCA")
|
||||
s = pickle.dumps(o)
|
||||
assert o.get_ext_data() == pickle.loads(s).get_ext_data()
|
||||
|
||||
def test_MSCSTemplateV2_too_few_parts(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4")
|
||||
|
||||
def test_MSCSTemplateV2_too_many_parts(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:100:200:300")
|
||||
|
||||
def test_MSCSTemplateV2_bad_oid(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("not_an_oid:1")
|
||||
|
||||
def test_MSCSTemplateV2_non_numeric_major_version(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:major:200")
|
||||
|
||||
def test_MSCSTemplateV2_non_numeric_minor_version(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:100:minor")
|
||||
|
||||
def test_MSCSTemplateV2_major_version_lt_zero(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:-1:200")
|
||||
|
||||
def test_MSCSTemplateV2_minor_version_lt_zero(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:100:-1")
|
||||
|
||||
def test_MSCSTemplateV2_major_version_gt_max(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:4294967296:200")
|
||||
|
||||
def test_MSCSTemplateV2_minor_version_gt_max(self):
|
||||
with pytest.raises(ValueError):
|
||||
cainstance.MSCSTemplateV2("1.2.3.4:100:4294967296")
|
||||
|
||||
def test_MSCSTemplateV2_good_major(self):
|
||||
o = cainstance.MSCSTemplateV2("1.2.3.4:4294967295")
|
||||
assert hexlify(o.get_ext_data()) == b'300c06032a0304020500ffffffff'
|
||||
|
||||
def test_MSCSTemplateV2_good_major_minor(self):
|
||||
o = cainstance.MSCSTemplateV2("1.2.3.4:4294967295:0")
|
||||
assert hexlify(o.get_ext_data()) \
|
||||
== b'300f06032a0304020500ffffffff020100'
|
||||
|
||||
def test_MSCSTemplateV2_pickle_roundtrip(self):
|
||||
o = cainstance.MSCSTemplateV2("1.2.3.4:4294967295:0")
|
||||
s = pickle.dumps(o)
|
||||
assert o.get_ext_data() == pickle.loads(s).get_ext_data()
|
||||
|
||||
def test_ExternalCAProfile_dispatch(self):
|
||||
"""
|
||||
Test that constructing ExternalCAProfile actually returns an
|
||||
instance of the appropriate subclass.
|
||||
"""
|
||||
assert isinstance(
|
||||
cainstance.ExternalCAProfile("MySubCA"),
|
||||
cainstance.MSCSTemplateV1)
|
||||
assert isinstance(
|
||||
cainstance.ExternalCAProfile("1.2.3.4:100"),
|
||||
cainstance.MSCSTemplateV2)
|
||||
|
||||
def test_write_pkispawn_config_file_MSCSTemplateV1(self):
|
||||
template = cainstance.MSCSTemplateV1(u"SubCA")
|
||||
expected = (
|
||||
'[CA]\n'
|
||||
'pki_req_ext_oid = 1.3.6.1.4.1.311.20.2\n'
|
||||
'pki_req_ext_data = 1e0a00530075006200430041\n\n'
|
||||
)
|
||||
self._test_write_pkispawn_config_file(template, expected)
|
||||
|
||||
def test_write_pkispawn_config_file_MSCSTemplateV2(self):
|
||||
template = cainstance.MSCSTemplateV2(u"1.2.3.4:4294967295")
|
||||
expected = (
|
||||
'[CA]\n'
|
||||
'pki_req_ext_oid = 1.3.6.1.4.1.311.21.7\n'
|
||||
'pki_req_ext_data = 300c06032a0304020500ffffffff\n\n'
|
||||
)
|
||||
self._test_write_pkispawn_config_file(template, expected)
|
||||
|
||||
def _test_write_pkispawn_config_file(self, template, expected):
|
||||
"""
|
||||
Test that the values we read from an ExternalCAProfile
|
||||
object can be used to produce a reasonable-looking pkispawn
|
||||
configuration.
|
||||
"""
|
||||
config = RawConfigParser()
|
||||
config.optionxform = str
|
||||
config.add_section("CA")
|
||||
config.set("CA", "pki_req_ext_oid", template.ext_oid)
|
||||
config.set("CA", "pki_req_ext_data",
|
||||
hexlify(template.get_ext_data()).decode('ascii'))
|
||||
out = StringIO()
|
||||
config.write(out)
|
||||
assert out.getvalue() == expected
|
||||
38
ipatests/test_ipaserver/test_install/test_service.py
Normal file
38
ipatests/test_ipaserver/test_install/test_service.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# Authors:
|
||||
# Petr Viktorin <pviktori@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2014 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Tests for the `ipaserver.service` module.
|
||||
"""
|
||||
|
||||
from ipaserver.install import service
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.tier0
|
||||
def test_format_seconds():
|
||||
assert service.format_seconds(0) == '0 seconds'
|
||||
assert service.format_seconds(1) == '1 second'
|
||||
assert service.format_seconds(2) == '2 seconds'
|
||||
assert service.format_seconds(11) == '11 seconds'
|
||||
assert service.format_seconds(60) == '1 minute'
|
||||
assert service.format_seconds(61) == '1 minute 1 second'
|
||||
assert service.format_seconds(62) == '1 minute 2 seconds'
|
||||
assert service.format_seconds(120) == '2 minutes'
|
||||
assert service.format_seconds(125) == '2 minutes 5 seconds'
|
||||
279
ipatests/test_ipaserver/test_ipap11helper.py
Normal file
279
ipatests/test_ipaserver/test_ipap11helper.py
Normal file
@@ -0,0 +1,279 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
"""
|
||||
Test the `ipapython/ipap11helper/p11helper.c` module.
|
||||
"""
|
||||
|
||||
|
||||
from binascii import hexlify
|
||||
import os
|
||||
import os.path
|
||||
import logging
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
from ipaserver import p11helper as _ipap11helper
|
||||
|
||||
pytestmark = pytest.mark.tier0
|
||||
|
||||
CONFIG_DATA = """
|
||||
# SoftHSM v2 configuration file
|
||||
directories.tokendir = %s/tokens
|
||||
objectstore.backend = file
|
||||
"""
|
||||
|
||||
LIBSOFTHSM = paths.LIBSOFTHSM2_SO
|
||||
SOFTHSM2_UTIL = paths.SOFTHSM2_UTIL
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
log = logging.getLogger('t')
|
||||
|
||||
|
||||
master_key_label = u"master-ž" # random non-ascii character to test unicode
|
||||
master_key_id = "m"
|
||||
replica1_key_label = u"replica1"
|
||||
replica1_key_id = "id1"
|
||||
replica1_import_label = u"replica1-import"
|
||||
replica1_import_id = "id1-import"
|
||||
replica1_new_label = u"replica1-new-label-ž"
|
||||
replica2_key_label = u"replica2"
|
||||
replica2_key_id = "id2"
|
||||
replica_non_existent_label = u"replica-nonexistent"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def p11(request):
|
||||
token_path = tempfile.mkdtemp(prefix='pytest_', suffix='_pkcs11')
|
||||
os.chdir(token_path)
|
||||
os.mkdir('tokens')
|
||||
|
||||
with open('softhsm2.conf', 'w') as cfg:
|
||||
cfg.write(CONFIG_DATA % token_path)
|
||||
os.environ['SOFTHSM2_CONF'] = os.path.join(token_path, 'softhsm2.conf')
|
||||
subprocess.check_call([SOFTHSM2_UTIL, '--init-token', '--free',
|
||||
'--label', 'test', '--pin', '1234', '--so-pin',
|
||||
'1234'])
|
||||
|
||||
try:
|
||||
p11 = _ipap11helper.P11_Helper('test', "1234", LIBSOFTHSM)
|
||||
except _ipap11helper.Error:
|
||||
pytest.fail('Failed to initialize the helper object.', pytrace=False)
|
||||
|
||||
def fin():
|
||||
try:
|
||||
p11.finalize()
|
||||
except _ipap11helper.Error:
|
||||
pytest.fail('Failed to finalize the helper object.', pytrace=False)
|
||||
finally:
|
||||
subprocess.call(
|
||||
[SOFTHSM2_UTIL, '--delete-token', '--label', 'test'])
|
||||
del os.environ['SOFTHSM2_CONF']
|
||||
|
||||
request.addfinalizer(fin)
|
||||
|
||||
return p11
|
||||
|
||||
|
||||
class test_p11helper(object):
|
||||
def test_generate_master_key(self, p11):
|
||||
assert p11.generate_master_key(master_key_label, master_key_id,
|
||||
key_length=16, cka_wrap=True,
|
||||
cka_unwrap=True)
|
||||
|
||||
def test_search_for_master_key(self, p11):
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label, id=master_key_id)
|
||||
assert len(master_key) == 1, "The master key should exist."
|
||||
|
||||
def test_generate_replica_key_pair(self, p11):
|
||||
assert p11.generate_replica_key_pair(replica1_key_label,
|
||||
replica1_key_id,
|
||||
pub_cka_wrap=True,
|
||||
priv_cka_unwrap=True)
|
||||
|
||||
def test_find_key(self, p11):
|
||||
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica1_key_label, cka_wrap=True)
|
||||
assert len(rep1_pub) == 1, ("replica key pair has to contain "
|
||||
"1 pub key instead of %s" % len(rep1_pub))
|
||||
|
||||
rep1_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
||||
label=replica1_key_label, cka_unwrap=True)
|
||||
assert len(rep1_priv) == 1, ("replica key pair has to contain 1 "
|
||||
"private key instead of %s" %
|
||||
len(rep1_priv))
|
||||
|
||||
def test_find_key_by_uri(self, p11):
|
||||
rep1_pub = p11.find_keys(uri="pkcs11:object=replica1;objecttype=public")
|
||||
assert len(rep1_pub) == 1, ("replica key pair has to contain 1 pub "
|
||||
"key instead of %s" % len(rep1_pub))
|
||||
|
||||
def test_get_attribute_from_object(self, p11):
|
||||
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica1_key_label, cka_wrap=True)[0]
|
||||
|
||||
iswrap = p11.get_attribute(rep1_pub, _ipap11helper.CKA_WRAP)
|
||||
assert iswrap is True, "replica public key has to have CKA_WRAP = TRUE"
|
||||
|
||||
def test_generate_replica_keypair_with_extractable_private_key(self, p11):
|
||||
assert p11.generate_replica_key_pair(replica2_key_label,
|
||||
replica2_key_id,
|
||||
pub_cka_wrap=True,
|
||||
priv_cka_unwrap=True,
|
||||
priv_cka_extractable=True)
|
||||
|
||||
def test_find_key_on_nonexistent_key_pair(self, p11):
|
||||
test_list = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica_non_existent_label)
|
||||
assert len(test_list) == 0, ("list should be empty because label "
|
||||
"'%s' should not exist" %
|
||||
replica_non_existent_label)
|
||||
|
||||
def test_export_import_of_public_key(self, p11):
|
||||
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica1_key_label, cka_wrap=True)[0]
|
||||
pub = p11.export_public_key(rep1_pub)
|
||||
|
||||
log.debug("Exported public key %s", hexlify(pub))
|
||||
with open("public_key.asn1.der", "wb") as f:
|
||||
f.write(pub)
|
||||
|
||||
rep1_pub_import = p11.import_public_key(replica1_import_label,
|
||||
replica1_import_id,
|
||||
pub,
|
||||
cka_wrap=True)
|
||||
log.debug('imported replica 1 public key: %s', rep1_pub_import)
|
||||
|
||||
# test public key import
|
||||
rep1_modulus_orig = p11.get_attribute(rep1_pub,
|
||||
_ipap11helper.CKA_MODULUS)
|
||||
rep1_modulus_import = p11.get_attribute(rep1_pub_import,
|
||||
_ipap11helper.CKA_MODULUS)
|
||||
log.debug('rep1_modulus_orig = 0x%s', hexlify(rep1_modulus_orig))
|
||||
log.debug('rep1_modulus_import = 0x%s', hexlify(rep1_modulus_import))
|
||||
assert rep1_modulus_import == rep1_modulus_orig
|
||||
|
||||
rep1_pub_exp_orig = p11.get_attribute(
|
||||
rep1_pub, _ipap11helper.CKA_PUBLIC_EXPONENT)
|
||||
rep1_pub_exp_import = p11.get_attribute(
|
||||
rep1_pub_import, _ipap11helper.CKA_PUBLIC_EXPONENT)
|
||||
log.debug('rep1_pub_exp_orig = 0x%s', hexlify(rep1_pub_exp_orig))
|
||||
log.debug('rep1_pub_exp_import = 0x%s', hexlify(rep1_pub_exp_import))
|
||||
assert rep1_pub_exp_import == rep1_pub_exp_orig
|
||||
|
||||
def test_wrap_unwrap_key_by_master_key_with_AES(self, p11):
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label, id=master_key_id)[0]
|
||||
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
||||
label=replica2_key_label, cka_unwrap=True)[0]
|
||||
|
||||
log.debug("wrapping dnssec priv key by master key")
|
||||
wrapped_priv = p11.export_wrapped_key(
|
||||
rep2_priv, master_key, _ipap11helper.MECH_AES_KEY_WRAP_PAD
|
||||
)
|
||||
assert wrapped_priv
|
||||
|
||||
log.debug("wrapped_dnssec priv key: %s", hexlify(wrapped_priv))
|
||||
with open("wrapped_priv.der", "wb") as f:
|
||||
f.write(wrapped_priv)
|
||||
|
||||
assert p11.import_wrapped_private_key(
|
||||
u'test_import_wrapped_priv',
|
||||
'1',
|
||||
wrapped_priv,
|
||||
master_key,
|
||||
_ipap11helper.MECH_AES_KEY_WRAP_PAD,
|
||||
_ipap11helper.KEY_TYPE_RSA
|
||||
)
|
||||
|
||||
def test_wrap_unwrap_key_by_master_key_with_RSA_PKCS(self, p11):
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label, id=master_key_id)[0]
|
||||
rep2_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica2_key_label, cka_wrap=True)[0]
|
||||
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
||||
label=replica2_key_label, cka_unwrap=True)[0]
|
||||
|
||||
wrapped = p11.export_wrapped_key(master_key,
|
||||
rep2_pub,
|
||||
_ipap11helper.MECH_RSA_PKCS)
|
||||
assert wrapped
|
||||
|
||||
log.debug("wrapped key MECH_RSA_PKCS (secret master wrapped by pub "
|
||||
"key): %s", hexlify(wrapped))
|
||||
assert p11.import_wrapped_secret_key(u'test_import_wrapped',
|
||||
'2',
|
||||
wrapped,
|
||||
rep2_priv,
|
||||
_ipap11helper.MECH_RSA_PKCS,
|
||||
_ipap11helper.KEY_TYPE_AES)
|
||||
|
||||
def test_wrap_unwrap_by_master_key_with_RSA_PKCS_OAEP(self, p11):
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label, id=master_key_id)[0]
|
||||
rep2_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica2_key_label, cka_wrap=True)[0]
|
||||
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
||||
label=replica2_key_label, cka_unwrap=True)[0]
|
||||
|
||||
wrapped = p11.export_wrapped_key(master_key,
|
||||
rep2_pub,
|
||||
_ipap11helper.MECH_RSA_PKCS_OAEP)
|
||||
assert wrapped
|
||||
|
||||
log.debug("wrapped key MECH_RSA_PKCS_OAEP (secret master wrapped by "
|
||||
"pub key): %s", hexlify(wrapped))
|
||||
|
||||
assert p11.import_wrapped_secret_key(u'test_import_wrapped',
|
||||
'3',
|
||||
wrapped,
|
||||
rep2_priv,
|
||||
_ipap11helper.MECH_RSA_PKCS_OAEP,
|
||||
_ipap11helper.KEY_TYPE_AES)
|
||||
|
||||
def test_set_attribute_on_object(self, p11):
|
||||
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica1_key_label, cka_wrap=True)[0]
|
||||
test_label = replica1_new_label
|
||||
|
||||
p11.set_attribute(rep1_pub, _ipap11helper.CKA_LABEL, test_label)
|
||||
assert p11.get_attribute(rep1_pub, _ipap11helper.CKA_LABEL) \
|
||||
== test_label, "The labels do not match."
|
||||
|
||||
def test_do_not_generate_identical_master_keys(self, p11):
|
||||
with pytest.raises(_ipap11helper.DuplicationError):
|
||||
p11.generate_master_key(master_key_label, master_key_id,
|
||||
key_length=16)
|
||||
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label)
|
||||
assert len(master_key) == 1, ("There shouldn't be multiple keys "
|
||||
"with the same label.")
|
||||
|
||||
def test_delete_key(self, p11):
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label, id=master_key_id)[0]
|
||||
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica1_new_label, cka_wrap=True)[0]
|
||||
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
||||
label=replica2_key_label, cka_unwrap=True)[0]
|
||||
|
||||
for key in (rep1_pub, rep2_priv, master_key):
|
||||
p11.delete_key(key)
|
||||
|
||||
master_key = p11.find_keys(_ipap11helper.KEY_CLASS_SECRET_KEY,
|
||||
label=master_key_label, id=master_key_id)
|
||||
assert len(master_key) == 0, "The master key should be deleted."
|
||||
rep1_pub = p11.find_keys(_ipap11helper.KEY_CLASS_PUBLIC_KEY,
|
||||
label=replica1_new_label, cka_wrap=True)
|
||||
assert len(rep1_pub) == 0, ("The public key of replica1 pair should "
|
||||
"be deleted.")
|
||||
rep2_priv = p11.find_keys(_ipap11helper.KEY_CLASS_PRIVATE_KEY,
|
||||
label=replica2_key_label, cka_unwrap=True)
|
||||
assert len(rep2_priv) == 0, ("The private key of replica2 pair should"
|
||||
" be deleted.")
|
||||
126
ipatests/test_ipaserver/test_kadmin.py
Normal file
126
ipatests/test_ipaserver/test_kadmin.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#
|
||||
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
"""
|
||||
Test suite for creating principals via kadmin.local and modifying their keys
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
import tempfile
|
||||
|
||||
from ipalib import api
|
||||
|
||||
from ipaserver.install import installutils
|
||||
from ipatests.test_util import yield_fixture
|
||||
|
||||
|
||||
@yield_fixture()
|
||||
def keytab():
|
||||
fd, keytab_path = tempfile.mkstemp(suffix='.keytab')
|
||||
os.close(fd)
|
||||
|
||||
try:
|
||||
yield keytab_path
|
||||
finally:
|
||||
try:
|
||||
os.remove(keytab_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def service_in_kerberos_subtree(request):
|
||||
princ = u'svc1/{0.host}@{0.realm}'.format(api.env)
|
||||
installutils.kadmin_addprinc(princ)
|
||||
|
||||
def fin():
|
||||
try:
|
||||
installutils.kadmin(
|
||||
'delprinc -force {}'.format(princ))
|
||||
except Exception:
|
||||
pass
|
||||
request.addfinalizer(fin)
|
||||
return princ
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def service_in_service_subtree(request):
|
||||
princ = u'svc2/{0.host}@{0.realm}'.format(api.env)
|
||||
rpcclient = api.Backend.rpcclient
|
||||
was_connected = rpcclient.isconnected()
|
||||
|
||||
if not was_connected:
|
||||
rpcclient.connect()
|
||||
|
||||
api.Command.service_add(princ)
|
||||
|
||||
def fin():
|
||||
try:
|
||||
api.Command.service_del(princ)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if not was_connected:
|
||||
rpcclient.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
request.addfinalizer(fin)
|
||||
return princ
|
||||
|
||||
|
||||
@pytest.fixture(params=[service_in_kerberos_subtree,
|
||||
service_in_service_subtree])
|
||||
def service(request):
|
||||
return request.param(request)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.getuid() != 0, reason="kadmin.local is accesible only to root")
|
||||
class TestKadmin(object):
|
||||
def assert_success(self, command, *args):
|
||||
"""
|
||||
Since kadmin.local returns 0 also when internal errors occur, we have
|
||||
to catch the command's stderr and check that it is empty
|
||||
"""
|
||||
result = command(*args)
|
||||
assert not result.error_output
|
||||
|
||||
def test_create_keytab(self, service, keytab):
|
||||
"""
|
||||
tests that ktadd command works for both types of services
|
||||
"""
|
||||
self.assert_success(
|
||||
installutils.create_keytab,
|
||||
keytab,
|
||||
service)
|
||||
|
||||
def test_change_key(self, service, keytab):
|
||||
"""
|
||||
tests that both types of service can have passwords changed using
|
||||
kadmin
|
||||
"""
|
||||
self.assert_success(
|
||||
installutils.create_keytab,
|
||||
keytab,
|
||||
service)
|
||||
self.assert_success(
|
||||
installutils.kadmin,
|
||||
'change_password -randkey {}'.format(service))
|
||||
|
||||
def test_append_key(self, service, keytab):
|
||||
"""
|
||||
Tests that we can create a new keytab for both service types and then
|
||||
append new keys to it
|
||||
"""
|
||||
self.assert_success(
|
||||
installutils.create_keytab,
|
||||
keytab,
|
||||
service)
|
||||
self.assert_success(
|
||||
installutils.create_keytab,
|
||||
keytab,
|
||||
service)
|
||||
314
ipatests/test_ipaserver/test_ldap.py
Normal file
314
ipatests/test_ipaserver/test_ldap.py
Normal file
@@ -0,0 +1,314 @@
|
||||
# Authors:
|
||||
# Rob Crittenden <rcritten@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2010 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Test some simple LDAP requests using the ldap2 backend
|
||||
|
||||
# This fetches a certificate from a host principal so we can ensure that the
|
||||
# schema is working properly. We know this because the schema will tell the
|
||||
# encoder not to utf-8 encode binary attributes.
|
||||
|
||||
# The DM password needs to be set in ~/.ipa/.dmpw
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
import nose
|
||||
from nose.tools import assert_raises # pylint: disable=E0611
|
||||
import six
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipaserver.plugins.ldap2 import ldap2, AUTOBIND_DISABLED
|
||||
from ipalib import api, create_api, errors
|
||||
from ipapython.dn import DN
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
@pytest.mark.tier0
|
||||
@pytest.mark.needs_ipaapi
|
||||
class test_ldap(object):
|
||||
"""
|
||||
Test various LDAP client bind methods.
|
||||
"""
|
||||
|
||||
def setup(self):
|
||||
self.conn = None
|
||||
self.ldapuri = api.env.ldap_uri
|
||||
self.dn = DN(('krbprincipalname','ldap/%s@%s' % (api.env.host, api.env.realm)),
|
||||
('cn','services'),('cn','accounts'),api.env.basedn)
|
||||
|
||||
def teardown(self):
|
||||
if self.conn and self.conn.isconnected():
|
||||
self.conn.disconnect()
|
||||
|
||||
def test_anonymous(self):
|
||||
"""
|
||||
Test an anonymous LDAP bind using ldap2
|
||||
"""
|
||||
self.conn = ldap2(api)
|
||||
self.conn.connect(autobind=AUTOBIND_DISABLED)
|
||||
dn = api.env.basedn
|
||||
entry_attrs = self.conn.get_entry(dn, ['associateddomain'])
|
||||
domain = entry_attrs.single_value['associateddomain']
|
||||
assert domain == api.env.domain
|
||||
|
||||
def test_GSSAPI(self):
|
||||
"""
|
||||
Test a GSSAPI LDAP bind using ldap2
|
||||
"""
|
||||
self.conn = ldap2(api)
|
||||
self.conn.connect(autobind=AUTOBIND_DISABLED)
|
||||
entry_attrs = self.conn.get_entry(self.dn, ['usercertificate'])
|
||||
cert = entry_attrs.get('usercertificate')[0]
|
||||
assert cert.serial_number is not None
|
||||
|
||||
def test_simple(self):
|
||||
"""
|
||||
Test a simple LDAP bind using ldap2
|
||||
"""
|
||||
pwfile = api.env.dot_ipa + os.sep + ".dmpw"
|
||||
if os.path.isfile(pwfile):
|
||||
with open(pwfile, "r") as fp:
|
||||
dm_password = fp.read().rstrip()
|
||||
else:
|
||||
raise nose.SkipTest("No directory manager password in %s" % pwfile)
|
||||
self.conn = ldap2(api)
|
||||
self.conn.connect(bind_dn=DN(('cn', 'directory manager')), bind_pw=dm_password)
|
||||
entry_attrs = self.conn.get_entry(self.dn, ['usercertificate'])
|
||||
cert = entry_attrs.get('usercertificate')[0]
|
||||
assert cert.serial_number is not None
|
||||
|
||||
def test_Backend(self):
|
||||
"""
|
||||
Test using the ldap2 Backend directly (ala ipa-server-install)
|
||||
"""
|
||||
|
||||
# Create our own api because the one generated for the tests is
|
||||
# a client-only api. Then we register in the commands and objects
|
||||
# we need for the test.
|
||||
myapi = create_api(mode=None)
|
||||
myapi.bootstrap(context='cli', in_server=True, confdir=paths.ETC_IPA)
|
||||
myapi.finalize()
|
||||
|
||||
pwfile = api.env.dot_ipa + os.sep + ".dmpw"
|
||||
if os.path.isfile(pwfile):
|
||||
with open(pwfile, "r") as fp:
|
||||
dm_password = fp.read().rstrip()
|
||||
else:
|
||||
raise nose.SkipTest("No directory manager password in %s" % pwfile)
|
||||
myapi.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password)
|
||||
|
||||
result = myapi.Command['service_show']('ldap/%s@%s' % (api.env.host, api.env.realm,))
|
||||
entry_attrs = result['result']
|
||||
cert = entry_attrs.get('usercertificate')[0]
|
||||
assert cert.serial_number is not None
|
||||
|
||||
def test_autobind(self):
|
||||
"""
|
||||
Test an autobind LDAP bind using ldap2
|
||||
"""
|
||||
self.conn = ldap2(api)
|
||||
try:
|
||||
self.conn.connect(autobind=True)
|
||||
except errors.ACIError:
|
||||
raise nose.SkipTest("Only executed as root")
|
||||
entry_attrs = self.conn.get_entry(self.dn, ['usercertificate'])
|
||||
cert = entry_attrs.get('usercertificate')[0]
|
||||
assert cert.serial_number is not None
|
||||
|
||||
|
||||
@pytest.mark.tier0
|
||||
@pytest.mark.needs_ipaapi
|
||||
class test_LDAPEntry(object):
|
||||
"""
|
||||
Test the LDAPEntry class
|
||||
"""
|
||||
cn1 = [u'test1']
|
||||
cn2 = [u'test2']
|
||||
dn1 = DN(('cn', cn1[0]))
|
||||
dn2 = DN(('cn', cn2[0]))
|
||||
|
||||
def setup(self):
|
||||
self.ldapuri = api.env.ldap_uri
|
||||
self.conn = ldap2(api)
|
||||
self.conn.connect(autobind=AUTOBIND_DISABLED)
|
||||
|
||||
self.entry = self.conn.make_entry(self.dn1, cn=self.cn1)
|
||||
|
||||
def teardown(self):
|
||||
if self.conn and self.conn.isconnected():
|
||||
self.conn.disconnect()
|
||||
|
||||
def test_entry(self):
|
||||
e = self.entry
|
||||
assert e.dn is self.dn1
|
||||
assert u'cn' in e
|
||||
assert u'cn' in e.keys()
|
||||
assert 'CN' in e
|
||||
if six.PY2:
|
||||
assert 'CN' not in e.keys()
|
||||
else:
|
||||
assert 'CN' in e.keys()
|
||||
assert 'commonName' in e
|
||||
if six.PY2:
|
||||
assert 'commonName' not in e.keys()
|
||||
else:
|
||||
assert 'commonName' in e.keys()
|
||||
assert e['CN'] is self.cn1
|
||||
assert e['CN'] is e[u'cn']
|
||||
|
||||
e.dn = self.dn2
|
||||
assert e.dn is self.dn2
|
||||
|
||||
def test_set_attr(self):
|
||||
e = self.entry
|
||||
e['commonName'] = self.cn2
|
||||
assert u'cn' in e
|
||||
assert u'cn' in e.keys()
|
||||
assert 'CN' in e
|
||||
if six.PY2:
|
||||
assert 'CN' not in e.keys()
|
||||
else:
|
||||
assert 'CN' in e.keys()
|
||||
assert 'commonName' in e
|
||||
if six.PY2:
|
||||
assert 'commonName' not in e.keys()
|
||||
else:
|
||||
assert 'commonName' in e.keys()
|
||||
assert e['CN'] is self.cn2
|
||||
assert e['CN'] is e[u'cn']
|
||||
|
||||
def test_del_attr(self):
|
||||
e = self.entry
|
||||
del e['CN']
|
||||
assert 'CN' not in e
|
||||
assert 'CN' not in e.keys()
|
||||
assert u'cn' not in e
|
||||
assert u'cn' not in e.keys()
|
||||
assert 'commonName' not in e
|
||||
assert 'commonName' not in e.keys()
|
||||
|
||||
def test_popitem(self):
|
||||
e = self.entry
|
||||
assert e.popitem() == ('cn', self.cn1)
|
||||
assert list(e) == []
|
||||
|
||||
def test_setdefault(self):
|
||||
e = self.entry
|
||||
assert e.setdefault('cn', self.cn2) == self.cn1
|
||||
assert e['cn'] == self.cn1
|
||||
assert e.setdefault('xyz', self.cn2) == self.cn2
|
||||
assert e['xyz'] == self.cn2
|
||||
|
||||
def test_update(self):
|
||||
e = self.entry
|
||||
e.update({'cn': self.cn2}, xyz=self.cn2)
|
||||
assert e['cn'] == self.cn2
|
||||
assert e['xyz'] == self.cn2
|
||||
|
||||
def test_pop(self):
|
||||
e = self.entry
|
||||
assert e.pop('cn') == self.cn1
|
||||
assert 'cn' not in e
|
||||
assert e.pop('cn', 'default') is 'default'
|
||||
with assert_raises(KeyError):
|
||||
e.pop('cn')
|
||||
|
||||
def test_clear(self):
|
||||
e = self.entry
|
||||
e.clear()
|
||||
assert not e
|
||||
assert 'cn' not in e
|
||||
|
||||
@pytest.mark.skipif(sys.version_info >= (3, 0), reason="Python 2 only")
|
||||
def test_has_key(self):
|
||||
e = self.entry
|
||||
assert not e.has_key('xyz')
|
||||
assert e.has_key('cn')
|
||||
assert e.has_key('COMMONNAME')
|
||||
|
||||
def test_in(self):
|
||||
e = self.entry
|
||||
assert 'xyz' not in e
|
||||
assert 'cn' in e
|
||||
assert 'COMMONNAME' in e
|
||||
|
||||
def test_get(self):
|
||||
e = self.entry
|
||||
assert e.get('cn') == self.cn1
|
||||
assert e.get('commonname') == self.cn1
|
||||
assert e.get('COMMONNAME', 'default') == self.cn1
|
||||
assert e.get('bad key', 'default') == 'default'
|
||||
|
||||
def test_single_value(self):
|
||||
e = self.entry
|
||||
assert e.single_value['cn'] == self.cn1[0]
|
||||
assert e.single_value['commonname'] == self.cn1[0]
|
||||
assert e.single_value.get('COMMONNAME', 'default') == self.cn1[0]
|
||||
assert e.single_value.get('bad key', 'default') == 'default'
|
||||
|
||||
def test_sync(self):
|
||||
e = self.entry
|
||||
|
||||
nice = e['test'] = [1, 2, 3]
|
||||
assert e['test'] is nice
|
||||
|
||||
raw = e.raw['test']
|
||||
assert raw == [b'1', b'2', b'3']
|
||||
|
||||
nice.remove(1)
|
||||
assert e.raw['test'] is raw
|
||||
assert raw == [b'2', b'3']
|
||||
|
||||
raw.append(b'4')
|
||||
assert e['test'] is nice
|
||||
assert nice == [2, 3, u'4']
|
||||
|
||||
nice.remove(2)
|
||||
raw.append(b'5')
|
||||
assert nice == [3, u'4']
|
||||
assert raw == [b'2', b'3', b'4', b'5']
|
||||
assert e['test'] is nice
|
||||
assert e.raw['test'] is raw
|
||||
assert nice == [3, u'4', u'5']
|
||||
assert raw == [b'3', b'4', b'5']
|
||||
|
||||
nice.insert(0, 2)
|
||||
raw.remove(b'4')
|
||||
assert nice == [2, 3, u'4', u'5']
|
||||
assert raw == [b'3', b'5']
|
||||
assert e.raw['test'] is raw
|
||||
assert e['test'] is nice
|
||||
assert nice == [2, 3, u'5']
|
||||
assert raw == [b'3', b'5', b'2']
|
||||
|
||||
raw = [b'a', b'b']
|
||||
e.raw['test'] = raw
|
||||
assert e['test'] is not nice
|
||||
assert e['test'] == [u'a', u'b']
|
||||
|
||||
nice = 'not list'
|
||||
e['test'] = nice
|
||||
assert e['test'] is nice
|
||||
assert e.raw['test'] == [b'not list']
|
||||
|
||||
e.raw['test'].append(b'second')
|
||||
assert e['test'] == ['not list', u'second']
|
||||
150
ipatests/test_ipaserver/test_otptoken_import.py
Normal file
150
ipatests/test_ipaserver/test_otptoken_import.py
Normal file
@@ -0,0 +1,150 @@
|
||||
# Authors:
|
||||
# Nathaniel McCallum <npmccallum@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2014 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from ipaserver.install.ipa_otptoken_import import PSKCDocument, ValidationError
|
||||
from ipaserver.install.ipa_otptoken_import import convertHashName
|
||||
|
||||
basename = os.path.join(os.path.dirname(__file__), "data")
|
||||
|
||||
@pytest.mark.tier1
|
||||
class test_otptoken_import(object):
|
||||
def test_figure3(self):
|
||||
doc = PSKCDocument(os.path.join(basename, "pskc-figure3.xml"))
|
||||
assert doc.keyname is None
|
||||
assert [(t.id, t.options) for t in doc.getKeyPackages()] == \
|
||||
[(u'12345678', {
|
||||
'ipatokenotpkey': u'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ',
|
||||
'ipatokenvendor': u'Manufacturer',
|
||||
'ipatokenserial': u'987654321',
|
||||
'ipatokenhotpcounter': 0,
|
||||
'ipatokenotpdigits': 8,
|
||||
'type': u'hotp',
|
||||
})]
|
||||
|
||||
def test_figure4(self):
|
||||
doc = PSKCDocument(os.path.join(basename, "pskc-figure4.xml"))
|
||||
assert doc.keyname is None
|
||||
try:
|
||||
[(t.id, t.options) for t in doc.getKeyPackages()]
|
||||
except ValidationError: # Referenced keys are not supported.
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_figure5(self):
|
||||
doc = PSKCDocument(os.path.join(basename, "pskc-figure5.xml"))
|
||||
assert doc.keyname is None
|
||||
try:
|
||||
[(t.id, t.options) for t in doc.getKeyPackages()]
|
||||
except ValidationError: # PIN Policy is not supported.
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_figure6(self):
|
||||
doc = PSKCDocument(os.path.join(basename, "pskc-figure6.xml"))
|
||||
assert doc.keyname == 'Pre-shared-key'
|
||||
doc.setKey(codecs.decode('12345678901234567890123456789012', 'hex'))
|
||||
assert [(t.id, t.options) for t in doc.getKeyPackages()] == \
|
||||
[(u'12345678', {
|
||||
'ipatokenotpkey': u'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ',
|
||||
'ipatokenvendor': u'Manufacturer',
|
||||
'ipatokenserial': u'987654321',
|
||||
'ipatokenhotpcounter': 0,
|
||||
'ipatokenotpdigits': 8,
|
||||
'type': u'hotp'})]
|
||||
|
||||
def test_figure7(self):
|
||||
doc = PSKCDocument(os.path.join(basename, "pskc-figure7.xml"))
|
||||
assert doc.keyname == 'My Password 1'
|
||||
doc.setKey(b'qwerty')
|
||||
assert [(t.id, t.options) for t in doc.getKeyPackages()] == \
|
||||
[(u'123456', {
|
||||
'ipatokenotpkey': u'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ',
|
||||
'ipatokenvendor': u'TokenVendorAcme',
|
||||
'ipatokenserial': u'987654321',
|
||||
'ipatokenotpdigits': 8,
|
||||
'type': u'hotp'})]
|
||||
|
||||
def test_figure8(self):
|
||||
try:
|
||||
PSKCDocument(os.path.join(basename, "pskc-figure8.xml"))
|
||||
except NotImplementedError: # X.509 is not supported.
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_invalid(self):
|
||||
try:
|
||||
PSKCDocument(os.path.join(basename, "pskc-invalid.xml"))
|
||||
except ValueError: # File is invalid.
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_mini(self):
|
||||
try:
|
||||
doc = PSKCDocument(os.path.join(basename, "pskc-mini.xml"))
|
||||
for t in doc.getKeyPackages():
|
||||
t._PSKCKeyPackage__process()
|
||||
except ValidationError: # Unsupported token type.
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_full(self):
|
||||
doc = PSKCDocument(os.path.join(basename, "full.xml"))
|
||||
assert [(t.id, t.options) for t in doc.getKeyPackages()] == \
|
||||
[(u'KID1', {
|
||||
'ipatokenotpkey': u'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ',
|
||||
'ipatokennotafter': u'20060531000000Z',
|
||||
'ipatokennotbefore': u'20060501000000Z',
|
||||
'ipatokenserial': u'SerialNo-IssueNo',
|
||||
'ipatokentotpclockoffset': 60000,
|
||||
'ipatokenotpalgorithm': u'sha1',
|
||||
'ipatokenvendor': u'iana.dummy',
|
||||
'description': u'FriendlyName',
|
||||
'ipatokentotptimestep': 200,
|
||||
'ipatokenhotpcounter': 0,
|
||||
'ipatokenmodel': u'Model',
|
||||
'ipatokenotpdigits': 8,
|
||||
'type': u'hotp',
|
||||
})]
|
||||
|
||||
def test_valid_tokens(self):
|
||||
assert convertHashName('sha1') == u'sha1'
|
||||
assert convertHashName('hmac-sha1') == u'sha1'
|
||||
assert convertHashName('sha224') == u'sha224'
|
||||
assert convertHashName('hmac-sha224') == u'sha224'
|
||||
assert convertHashName('sha256') == u'sha256'
|
||||
assert convertHashName('hmac-sha256') == u'sha256'
|
||||
assert convertHashName('sha384') == u'sha384'
|
||||
assert convertHashName('hmac-sha384') == u'sha384'
|
||||
assert convertHashName('sha512') == u'sha512'
|
||||
assert convertHashName('hmac-sha512') == u'sha512'
|
||||
|
||||
def test_invalid_tokens(self):
|
||||
"""The conversion defaults to sha1 on unknown hashing"""
|
||||
assert convertHashName('something-sha256') == u'sha1'
|
||||
assert convertHashName('') == u'sha1'
|
||||
assert convertHashName(None) == u'sha1'
|
||||
263
ipatests/test_ipaserver/test_rpcserver.py
Normal file
263
ipatests/test_ipaserver/test_rpcserver.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# Authors:
|
||||
# Jason Gerard DeRose <jderose@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2008 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
Test the `ipaserver.rpc` module.
|
||||
"""
|
||||
|
||||
import json
|
||||
import pytest
|
||||
|
||||
import six
|
||||
|
||||
from ipatests.util import assert_equal, raises, PluginTester
|
||||
from ipalib import errors
|
||||
from ipaserver import rpcserver
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
pytestmark = pytest.mark.tier0
|
||||
|
||||
class StartResponse(object):
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.status = None
|
||||
self.headers = None
|
||||
|
||||
def __call__(self, status, headers):
|
||||
assert self.status is None
|
||||
assert self.headers is None
|
||||
assert isinstance(status, str)
|
||||
assert isinstance(headers, list)
|
||||
self.status = status
|
||||
self.headers = headers
|
||||
|
||||
|
||||
def test_not_found():
|
||||
api = 'the api instance'
|
||||
f = rpcserver.HTTP_Status(api)
|
||||
t = rpcserver._not_found_template
|
||||
s = StartResponse()
|
||||
|
||||
# Test with an innocent URL:
|
||||
url = '/ipa/foo/stuff'
|
||||
assert_equal(
|
||||
f.not_found(None, s, url, None),
|
||||
[(t % dict(url='/ipa/foo/stuff')).encode('utf-8')]
|
||||
)
|
||||
assert s.status == '404 Not Found'
|
||||
assert s.headers == [('Content-Type', 'text/html; charset=utf-8')]
|
||||
|
||||
# Test when URL contains any of '<>&'
|
||||
s.reset()
|
||||
url =' ' + '<script>do_bad_stuff();</script>'
|
||||
assert_equal(
|
||||
f.not_found(None, s, url, None),
|
||||
[(t % dict(
|
||||
url='&nbsp;<script>do_bad_stuff();</script>')
|
||||
).encode('utf-8')]
|
||||
)
|
||||
assert s.status == '404 Not Found'
|
||||
assert s.headers == [('Content-Type', 'text/html; charset=utf-8')]
|
||||
|
||||
|
||||
def test_bad_request():
|
||||
api = 'the api instance'
|
||||
f = rpcserver.HTTP_Status(api)
|
||||
t = rpcserver._bad_request_template
|
||||
s = StartResponse()
|
||||
|
||||
assert_equal(
|
||||
f.bad_request(None, s, 'illegal request'),
|
||||
[(t % dict(message='illegal request')).encode('utf-8')]
|
||||
)
|
||||
assert s.status == '400 Bad Request'
|
||||
assert s.headers == [('Content-Type', 'text/html; charset=utf-8')]
|
||||
|
||||
|
||||
def test_internal_error():
|
||||
api = 'the api instance'
|
||||
f = rpcserver.HTTP_Status(api)
|
||||
t = rpcserver._internal_error_template
|
||||
s = StartResponse()
|
||||
|
||||
assert_equal(
|
||||
f.internal_error(None, s, 'request failed'),
|
||||
[(t % dict(message='request failed')).encode('utf-8')]
|
||||
)
|
||||
assert s.status == '500 Internal Server Error'
|
||||
assert s.headers == [('Content-Type', 'text/html; charset=utf-8')]
|
||||
|
||||
|
||||
def test_unauthorized_error():
|
||||
api = 'the api instance'
|
||||
f = rpcserver.HTTP_Status(api)
|
||||
t = rpcserver._unauthorized_template
|
||||
s = StartResponse()
|
||||
|
||||
assert_equal(
|
||||
f.unauthorized(None, s, 'unauthorized', 'password-expired'),
|
||||
[(t % dict(message='unauthorized')).encode('utf-8')]
|
||||
)
|
||||
assert s.status == '401 Unauthorized'
|
||||
assert s.headers == [('Content-Type', 'text/html; charset=utf-8'),
|
||||
('X-IPA-Rejection-Reason', 'password-expired')]
|
||||
|
||||
|
||||
def test_params_2_args_options():
|
||||
"""
|
||||
Test the `ipaserver.rpcserver.params_2_args_options` function.
|
||||
"""
|
||||
f = rpcserver.params_2_args_options
|
||||
args = ('Hello', u'world!')
|
||||
options = dict(one=1, two=u'Two', three='Three')
|
||||
assert f(tuple()) == (tuple(), dict())
|
||||
assert f([args]) == (args, dict())
|
||||
assert f([args, options]) == (args, options)
|
||||
|
||||
|
||||
class test_session(object):
|
||||
klass = rpcserver.wsgi_dispatch
|
||||
|
||||
def test_route(self):
|
||||
def app1(environ, start_response):
|
||||
return (
|
||||
'from 1',
|
||||
[environ[k] for k in ('SCRIPT_NAME', 'PATH_INFO')]
|
||||
)
|
||||
|
||||
def app2(environ, start_response):
|
||||
return (
|
||||
'from 2',
|
||||
[environ[k] for k in ('SCRIPT_NAME', 'PATH_INFO')]
|
||||
)
|
||||
|
||||
api = 'the api instance'
|
||||
inst = self.klass(api)
|
||||
inst.mount(app1, '/foo/stuff')
|
||||
inst.mount(app2, '/bar')
|
||||
|
||||
d = dict(SCRIPT_NAME='/ipa', PATH_INFO='/foo/stuff')
|
||||
assert inst.route(d, None) == ('from 1', ['/ipa', '/foo/stuff'])
|
||||
|
||||
d = dict(SCRIPT_NAME='/ipa', PATH_INFO='/bar')
|
||||
assert inst.route(d, None) == ('from 2', ['/ipa', '/bar'])
|
||||
|
||||
def test_mount(self):
|
||||
def app1(environ, start_response):
|
||||
pass
|
||||
|
||||
def app2(environ, start_response):
|
||||
pass
|
||||
|
||||
# Test that mount works:
|
||||
api = 'the api instance'
|
||||
inst = self.klass(api)
|
||||
inst.mount(app1, 'foo')
|
||||
assert inst['foo'] is app1
|
||||
assert list(inst) == ['foo']
|
||||
|
||||
# Test that Exception is raise if trying override a mount:
|
||||
e = raises(Exception, inst.mount, app2, 'foo')
|
||||
assert str(e) == '%s.mount(): cannot replace %r with %r at %r' % (
|
||||
'wsgi_dispatch', app1, app2, 'foo'
|
||||
)
|
||||
|
||||
# Test mounting a second app:
|
||||
inst.mount(app2, 'bar')
|
||||
assert inst['bar'] is app2
|
||||
assert list(inst) == ['bar', 'foo']
|
||||
|
||||
|
||||
class test_xmlserver(PluginTester):
|
||||
"""
|
||||
Test the `ipaserver.rpcserver.xmlserver` plugin.
|
||||
"""
|
||||
|
||||
_plugin = rpcserver.xmlserver
|
||||
|
||||
def test_marshaled_dispatch(self): # FIXME
|
||||
self.instance('Backend', in_server=True)
|
||||
|
||||
|
||||
class test_jsonserver(PluginTester):
|
||||
"""
|
||||
Test the `ipaserver.rpcserver.jsonserver` plugin.
|
||||
"""
|
||||
|
||||
_plugin = rpcserver.jsonserver
|
||||
|
||||
def test_unmarshal(self):
|
||||
"""
|
||||
Test the `ipaserver.rpcserver.jsonserver.unmarshal` method.
|
||||
"""
|
||||
o, _api, _home = self.instance('Backend', in_server=True)
|
||||
|
||||
# Test with invalid JSON-data:
|
||||
e = raises(errors.JSONError, o.unmarshal, 'this wont work')
|
||||
if six.PY2:
|
||||
assert unicode(e.error) == 'No JSON object could be decoded'
|
||||
else:
|
||||
assert str(e.error).startswith('Expecting value: ')
|
||||
|
||||
# Test with non-dict type:
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps([1, 2, 3]))
|
||||
assert unicode(e.error) == 'Request must be a dict'
|
||||
|
||||
params = [[1, 2], dict(three=3, four=4)]
|
||||
# Test with missing method:
|
||||
d = dict(params=params, id=18)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert unicode(e.error) == 'Request is missing "method"'
|
||||
|
||||
# Test with missing params:
|
||||
d = dict(method='echo', id=18)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert unicode(e.error) == 'Request is missing "params"'
|
||||
|
||||
# Test with non-list params:
|
||||
for p in ('hello', dict(args=tuple(), options=dict())):
|
||||
d = dict(method='echo', id=18, params=p)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert unicode(e.error) == 'params must be a list'
|
||||
|
||||
# Test with other than 2 params:
|
||||
for p in ([], [tuple()], [None, dict(), tuple()]):
|
||||
d = dict(method='echo', id=18, params=p)
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert unicode(e.error) == 'params must contain [args, options]'
|
||||
|
||||
# Test when args is not a list:
|
||||
d = dict(method='echo', id=18, params=['args', dict()])
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert unicode(e.error) == 'params[0] (aka args) must be a list'
|
||||
|
||||
# Test when options is not a dict:
|
||||
d = dict(method='echo', id=18, params=[('hello', 'world'), 'options'])
|
||||
e = raises(errors.JSONError, o.unmarshal, json.dumps(d))
|
||||
assert unicode(e.error) == 'params[1] (aka options) must be a dict'
|
||||
|
||||
# Test with valid values:
|
||||
args = [u'jdoe']
|
||||
options = dict(givenname=u'John', sn='Doe')
|
||||
d = dict(method=u'user_add', params=(args, options), id=18)
|
||||
assert o.unmarshal(json.dumps(d)) == (u'user_add', args, options, 18)
|
||||
55
ipatests/test_ipaserver/test_secrets.py
Normal file
55
ipatests/test_ipaserver/test_secrets.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Copyright (C) 2015 FreeIPA Project Contributors - see LICENSE file
|
||||
|
||||
from __future__ import print_function
|
||||
from ipaserver.secrets.store import iSecStore, NAME_DB_MAP, NSSCertDB
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
|
||||
def _test_password_callback():
|
||||
with open('test-ipa-sec-store/pwfile') as f:
|
||||
password = f.read()
|
||||
return password
|
||||
|
||||
|
||||
class TestiSecStore(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
try:
|
||||
shutil.rmtree('test-ipa-sec-store')
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
testdir = 'test-ipa-sec-store'
|
||||
pwfile = os.path.join(testdir, 'pwfile')
|
||||
os.mkdir(testdir)
|
||||
with open(pwfile, 'w') as f:
|
||||
f.write('testpw')
|
||||
cls.certdb = os.path.join(testdir, 'certdb')
|
||||
os.mkdir(cls.certdb)
|
||||
cls.cert2db = os.path.join(testdir, 'cert2db')
|
||||
os.mkdir(cls.cert2db)
|
||||
seedfile = os.path.join(testdir, 'seedfile')
|
||||
with open(seedfile, 'wb') as f:
|
||||
seed = os.urandom(1024)
|
||||
f.write(seed)
|
||||
subprocess.call(['certutil', '-d', cls.certdb, '-N', '-f', pwfile])
|
||||
subprocess.call(['certutil', '-d', cls.cert2db, '-N', '-f', pwfile])
|
||||
subprocess.call(['certutil', '-d', cls.certdb, '-S', '-f', pwfile,
|
||||
'-s', 'CN=testCA', '-n', 'testCACert', '-x',
|
||||
'-t', 'CT,C,C', '-m', '1', '-z', seedfile])
|
||||
|
||||
def test_iSecStore(self):
|
||||
iss = iSecStore({})
|
||||
|
||||
NAME_DB_MAP['test'] = {
|
||||
'type': 'NSSDB',
|
||||
'path': self.certdb,
|
||||
'handler': NSSCertDB,
|
||||
'pwcallback': _test_password_callback,
|
||||
}
|
||||
value = iss.get('keys/test/testCACert')
|
||||
|
||||
NAME_DB_MAP['test']['path'] = self.cert2db
|
||||
iss.set('keys/test/testCACert', value)
|
||||
743
ipatests/test_ipaserver/test_serverroles.py
Normal file
743
ipatests/test_ipaserver/test_serverroles.py
Normal file
@@ -0,0 +1,743 @@
|
||||
#
|
||||
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
"""
|
||||
Tests for the serverroles backend
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
import ldap
|
||||
import pytest
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipalib import api, create_api, errors
|
||||
from ipapython.dn import DN
|
||||
|
||||
pytestmark = pytest.mark.needs_ipaapi
|
||||
|
||||
|
||||
def _make_service_entry(ldap_backend, dn, enabled=True, other_config=None):
|
||||
mods = {
|
||||
'objectClass': ['top', 'nsContainer', 'ipaConfigObject'],
|
||||
}
|
||||
if enabled:
|
||||
mods.update({'ipaConfigString': ['enabledService']})
|
||||
|
||||
if other_config is not None:
|
||||
mods.setdefault('ipaConfigString', [])
|
||||
mods['ipaConfigString'].extend(other_config)
|
||||
|
||||
return ldap_backend.make_entry(dn, **mods)
|
||||
|
||||
|
||||
def _make_master_entry(ldap_backend, dn, ca=False):
|
||||
mods = {
|
||||
'objectClass': [
|
||||
'top',
|
||||
'nsContainer',
|
||||
'ipaReplTopoManagedServer',
|
||||
'ipaSupportedDomainLevelConfig',
|
||||
'ipaConfigObject',
|
||||
],
|
||||
'ipaMaxDomainLevel': ['1'],
|
||||
'ipaMinDomainLevel': ['0'],
|
||||
'ipaReplTopoManagedsuffix': [str(api.env.basedn)]
|
||||
}
|
||||
if ca:
|
||||
mods['ipaReplTopoManagedsuffix'].append('o=ipaca')
|
||||
|
||||
return ldap_backend.make_entry(dn, **mods)
|
||||
|
||||
_adtrust_agents = DN(
|
||||
('cn', 'adtrust agents'),
|
||||
('cn', 'sysaccounts'),
|
||||
('cn', 'etc'),
|
||||
api.env.basedn
|
||||
)
|
||||
|
||||
|
||||
master_data = {
|
||||
'ca-dns-dnssec-keymaster-pkinit-server': {
|
||||
'services': {
|
||||
'CA': {
|
||||
'enabled': True,
|
||||
},
|
||||
'DNS': {
|
||||
'enabled': True,
|
||||
},
|
||||
'DNSKeySync': {
|
||||
'enabled': True,
|
||||
},
|
||||
'DNSSEC': {
|
||||
'enabled': True,
|
||||
'config': ['DNSSecKeyMaster']
|
||||
},
|
||||
'KDC': {
|
||||
'enabled': True,
|
||||
'config': ['pkinitEnabled']
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master', 'CA server', 'DNS server']
|
||||
},
|
||||
'expected_attributes': {'DNS server': 'dnssec_key_master_server',
|
||||
'IPA master': 'pkinit_server_server'}
|
||||
},
|
||||
'ca-kra-renewal-master-pkinit-server': {
|
||||
'services': {
|
||||
'CA': {
|
||||
'enabled': True,
|
||||
'config': ['caRenewalMaster']
|
||||
},
|
||||
'KRA': {
|
||||
'enabled': True,
|
||||
},
|
||||
'KDC': {
|
||||
'enabled': True,
|
||||
'config': ['pkinitEnabled']
|
||||
},
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master', 'CA server', 'KRA server']
|
||||
},
|
||||
'expected_attributes': {'CA server': 'ca_renewal_master_server',
|
||||
'IPA master': 'pkinit_server_server'}
|
||||
},
|
||||
'dns-trust-agent': {
|
||||
'services': {
|
||||
'DNS': {
|
||||
'enabled': True,
|
||||
},
|
||||
'DNSKeySync': {
|
||||
'enabled': True,
|
||||
}
|
||||
},
|
||||
'attributes': {
|
||||
_adtrust_agents: {
|
||||
'member': ['host']
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master', 'DNS server', 'AD trust agent']
|
||||
}
|
||||
},
|
||||
'trust-agent': {
|
||||
'attributes': {
|
||||
_adtrust_agents: {
|
||||
'member': ['host']
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master', 'AD trust agent']
|
||||
}
|
||||
},
|
||||
'trust-controller-dns': {
|
||||
'services': {
|
||||
'ADTRUST': {
|
||||
'enabled': True,
|
||||
},
|
||||
'DNS': {
|
||||
'enabled': True,
|
||||
},
|
||||
'DNSKeySync': {
|
||||
'enabled': True,
|
||||
}
|
||||
},
|
||||
'attributes': {
|
||||
_adtrust_agents: {
|
||||
'member': ['host', 'cifs']
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master', 'AD trust agent', 'AD trust controller',
|
||||
'DNS server']
|
||||
}
|
||||
},
|
||||
'trust-controller-ca': {
|
||||
'services': {
|
||||
'ADTRUST': {
|
||||
'enabled': True,
|
||||
},
|
||||
'CA': {
|
||||
'enabled': True,
|
||||
},
|
||||
},
|
||||
'attributes': {
|
||||
_adtrust_agents: {
|
||||
'member': ['host', 'cifs']
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master', 'AD trust agent', 'AD trust controller',
|
||||
'CA server']
|
||||
}
|
||||
},
|
||||
'configured-ca': {
|
||||
'services': {
|
||||
'CA': {
|
||||
'enabled': False,
|
||||
},
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master'],
|
||||
'configured': ['CA server']
|
||||
}
|
||||
},
|
||||
'configured-dns': {
|
||||
'services': {
|
||||
'DNS': {
|
||||
'enabled': False,
|
||||
},
|
||||
'DNSKeySync': {
|
||||
'enabled': False,
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master'],
|
||||
'configured': ['DNS server']
|
||||
}
|
||||
},
|
||||
'mixed-state-dns': {
|
||||
'services': {
|
||||
'DNS': {
|
||||
'enabled': False
|
||||
},
|
||||
'DNSKeySync': {
|
||||
'enabled': True
|
||||
}
|
||||
},
|
||||
'expected_roles': {
|
||||
'enabled': ['IPA master'],
|
||||
'configured': ['DNS server']
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class MockMasterTopology(object):
|
||||
"""
|
||||
object that will set up and tear down entries in LDAP backend to mimic
|
||||
a presence of real IPA masters with services running on them.
|
||||
"""
|
||||
|
||||
ipamaster_services = [u'KDC', u'HTTP', u'KPASSWD']
|
||||
|
||||
def __init__(self, api_instance, domain_data):
|
||||
self.api = api_instance
|
||||
|
||||
self.domain = self.api.env.domain
|
||||
self.domain_data = domain_data
|
||||
self.masters_base = DN(
|
||||
self.api.env.container_masters, self.api.env.basedn)
|
||||
|
||||
self.test_master_dn = DN(
|
||||
('cn', self.api.env.host), self.api.env.container_masters,
|
||||
self.api.env.basedn)
|
||||
|
||||
self.ldap = self.api.Backend.ldap2
|
||||
|
||||
self.existing_masters = {
|
||||
m['cn'][0] for m in self.api.Command.server_find(
|
||||
u'', sizelimit=0,
|
||||
pkey_only=True,
|
||||
no_members=True,
|
||||
raw=True)['result']}
|
||||
|
||||
self.original_dns_configs = self._remove_test_host_attrs()
|
||||
|
||||
def iter_domain_data(self):
|
||||
MasterData = namedtuple('MasterData',
|
||||
['dn', 'fqdn', 'services', 'attrs'])
|
||||
for name in self.domain_data:
|
||||
fqdn = self.get_fqdn(name)
|
||||
master_dn = self.get_master_dn(name)
|
||||
master_services = self.domain_data[name].get('services', {})
|
||||
|
||||
master_attributes = self.domain_data[name].get('attributes', {})
|
||||
|
||||
yield MasterData(
|
||||
dn=master_dn,
|
||||
fqdn=fqdn,
|
||||
services=master_services,
|
||||
attrs=master_attributes
|
||||
)
|
||||
|
||||
def get_fqdn(self, name):
|
||||
return '.'.join([name, self.domain])
|
||||
|
||||
def get_master_dn(self, name):
|
||||
return DN(('cn', self.get_fqdn(name)), self.masters_base)
|
||||
|
||||
def get_service_dn(self, name, master_dn):
|
||||
return DN(('cn', name), master_dn)
|
||||
|
||||
def _add_host_entry(self, fqdn):
|
||||
self.api.Command.host_add(fqdn, force=True)
|
||||
self.api.Command.hostgroup_add_member(u'ipaservers', host=fqdn)
|
||||
|
||||
def _del_host_entry(self, fqdn):
|
||||
try:
|
||||
self.api.Command.host_del(fqdn)
|
||||
except errors.NotFound:
|
||||
pass
|
||||
|
||||
def _add_service_entry(self, service, fqdn):
|
||||
return self.api.Command.service_add(
|
||||
'/'.join([service, fqdn]),
|
||||
force=True
|
||||
)
|
||||
|
||||
def _del_service_entry(self, service, fqdn):
|
||||
try:
|
||||
self.api.Command.service_del(
|
||||
'/'.join([service, fqdn]),
|
||||
)
|
||||
except errors.NotFound:
|
||||
pass
|
||||
|
||||
def _add_svc_entries(self, master_dn, svc_desc):
|
||||
for name in svc_desc:
|
||||
svc_dn = self.get_service_dn(name, master_dn)
|
||||
svc_mods = svc_desc[name]
|
||||
|
||||
self.ldap.add_entry(
|
||||
_make_service_entry(
|
||||
self.ldap,
|
||||
svc_dn,
|
||||
enabled=svc_mods['enabled'],
|
||||
other_config=svc_mods.get('config', None)))
|
||||
|
||||
self._add_ipamaster_services(master_dn)
|
||||
|
||||
def _remove_svc_master_entries(self, master_dn):
|
||||
try:
|
||||
entries = self.ldap.get_entries(
|
||||
master_dn, ldap.SCOPE_SUBTREE
|
||||
)
|
||||
except errors.NotFound:
|
||||
return
|
||||
|
||||
if entries:
|
||||
entries.sort(key=lambda x: len(x.dn), reverse=True)
|
||||
for entry in entries:
|
||||
self.ldap.delete_entry(entry)
|
||||
|
||||
def _add_ipamaster_services(self, master_dn):
|
||||
"""
|
||||
add all the service entries which are part of the IPA Master role
|
||||
"""
|
||||
for svc_name in self.ipamaster_services:
|
||||
svc_dn = self.get_service_dn(svc_name, master_dn)
|
||||
try:
|
||||
self.ldap.get_entry(svc_dn)
|
||||
except errors.NotFound:
|
||||
self.ldap.add_entry(_make_service_entry(self.ldap, svc_dn))
|
||||
|
||||
def _add_members(self, dn, fqdn, member_attrs):
|
||||
entry_attrs = self.ldap.get_entry(dn)
|
||||
|
||||
value = entry_attrs.get('member', [])
|
||||
|
||||
for a in member_attrs:
|
||||
|
||||
if a == 'host':
|
||||
value.append(
|
||||
str(self.api.Object.host.get_dn(fqdn)))
|
||||
else:
|
||||
result = self._add_service_entry(a, fqdn)['result']
|
||||
value.append(str(result['dn']))
|
||||
|
||||
entry_attrs['member'] = value
|
||||
self.ldap.update_entry(entry_attrs)
|
||||
|
||||
def _remove_members(self, dn, fqdn, member_attrs):
|
||||
entry_attrs = self.ldap.get_entry(dn)
|
||||
|
||||
value = set(entry_attrs.get('member', []))
|
||||
|
||||
if not value:
|
||||
return
|
||||
|
||||
for a in member_attrs:
|
||||
|
||||
if a == 'host':
|
||||
try:
|
||||
value.remove(
|
||||
str(self.api.Object.host.get_dn(fqdn)))
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
value.remove(
|
||||
str(self.api.Object.service.get_dn(
|
||||
'/'.join([a, fqdn]))))
|
||||
except KeyError:
|
||||
pass
|
||||
self._del_service_entry(a, fqdn)
|
||||
|
||||
entry_attrs['member'] = list(value)
|
||||
|
||||
try:
|
||||
self.ldap.update_entry(entry_attrs)
|
||||
except (errors.NotFound, errors.EmptyModlist):
|
||||
pass
|
||||
|
||||
def _remove_test_host_attrs(self):
|
||||
original_dns_configs = []
|
||||
|
||||
for attr_name in (
|
||||
'caRenewalMaster', 'dnssecKeyMaster', 'pkinitEnabled'):
|
||||
try:
|
||||
svc_entry = self.ldap.find_entry_by_attr(
|
||||
'ipaConfigString', attr_name, 'ipaConfigObject',
|
||||
base_dn=self.test_master_dn)
|
||||
except errors.NotFound:
|
||||
continue
|
||||
else:
|
||||
original_dns_configs.append(
|
||||
(svc_entry.dn, list(svc_entry.get('ipaConfigString', [])))
|
||||
)
|
||||
svc_entry[u'ipaConfigString'].remove(attr_name)
|
||||
self.ldap.update_entry(svc_entry)
|
||||
|
||||
return original_dns_configs
|
||||
|
||||
def _restore_test_host_attrs(self):
|
||||
for dn, config in self.original_dns_configs:
|
||||
try:
|
||||
svc_entry = self.api.Backend.ldap2.get_entry(dn)
|
||||
svc_entry['ipaConfigString'] = config
|
||||
self.ldap.update_entry(svc_entry)
|
||||
except (errors.NotFound, errors.EmptyModlist):
|
||||
continue
|
||||
|
||||
def setup_data(self):
|
||||
for master_data in self.iter_domain_data():
|
||||
# create host
|
||||
self._add_host_entry(master_data.fqdn)
|
||||
|
||||
# create master
|
||||
self.ldap.add_entry(
|
||||
_make_master_entry(
|
||||
self.ldap,
|
||||
master_data.dn,
|
||||
ca='CA' in master_data.services))
|
||||
|
||||
# now add service entries
|
||||
self._add_svc_entries(master_data.dn, master_data.services)
|
||||
|
||||
# optionally add some attributes required e.g. by AD trust roles
|
||||
for entry_dn, attrs in master_data.attrs.items():
|
||||
if 'member' in attrs:
|
||||
self._add_members(
|
||||
entry_dn,
|
||||
master_data.fqdn,
|
||||
attrs['member']
|
||||
)
|
||||
|
||||
def teardown_data(self):
|
||||
for master_data in self.iter_domain_data():
|
||||
# first remove the master entries and service containers
|
||||
self._remove_svc_master_entries(master_data.dn)
|
||||
|
||||
# optionally clean up leftover attributes
|
||||
for entry_dn, attrs in master_data.attrs.items():
|
||||
if 'member' in attrs:
|
||||
self._remove_members(
|
||||
entry_dn,
|
||||
master_data.fqdn,
|
||||
attrs['member'],
|
||||
)
|
||||
|
||||
# finally remove host entry
|
||||
self._del_host_entry(master_data.fqdn)
|
||||
|
||||
self._restore_test_host_attrs()
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def mock_api(request):
|
||||
test_api = create_api(mode=None)
|
||||
test_api.bootstrap(in_server=True,
|
||||
ldap_uri=api.env.ldap_uri,
|
||||
confdir=paths.ETC_IPA)
|
||||
test_api.finalize()
|
||||
|
||||
if not test_api.Backend.ldap2.isconnected():
|
||||
test_api.Backend.ldap2.connect()
|
||||
|
||||
def finalize():
|
||||
test_api.Backend.ldap2.disconnect()
|
||||
|
||||
request.addfinalizer(finalize)
|
||||
|
||||
return test_api
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def mock_masters(request, mock_api):
|
||||
"""
|
||||
Populate the LDAP backend with test data
|
||||
"""
|
||||
|
||||
if not api.Backend.rpcclient.isconnected():
|
||||
api.Backend.rpcclient.connect()
|
||||
|
||||
master_topo = MockMasterTopology(mock_api, master_data)
|
||||
|
||||
def finalize():
|
||||
master_topo.teardown_data()
|
||||
if api.Backend.rpcclient.isconnected():
|
||||
api.Backend.rpcclient.disconnect()
|
||||
|
||||
request.addfinalizer(finalize)
|
||||
|
||||
master_topo.setup_data()
|
||||
|
||||
return master_topo
|
||||
|
||||
|
||||
def enabled_role_iter(master_data):
|
||||
for m, data in master_data.items():
|
||||
for role in data['expected_roles']['enabled']:
|
||||
yield m, role
|
||||
|
||||
|
||||
def provided_role_iter(master_data):
|
||||
for m, data in master_data.items():
|
||||
yield m, data['expected_roles']['enabled']
|
||||
|
||||
|
||||
def configured_role_iter(master_data):
|
||||
for m, data in master_data.items():
|
||||
if 'configured' in data['expected_roles']:
|
||||
for role in data['expected_roles']['configured']:
|
||||
yield m, role
|
||||
|
||||
|
||||
def role_provider_iter(master_data):
|
||||
result = {}
|
||||
for m, data in master_data.items():
|
||||
for role in data['expected_roles']['enabled']:
|
||||
if role not in result:
|
||||
result[role] = []
|
||||
|
||||
result[role].append(m)
|
||||
|
||||
for role_name, masters in result.items():
|
||||
yield role_name, masters
|
||||
|
||||
|
||||
def attribute_masters_iter(master_data):
|
||||
for m, data in master_data.items():
|
||||
if 'expected_attributes' in data:
|
||||
for assoc_role, attr in data['expected_attributes'].items():
|
||||
yield m, assoc_role, attr
|
||||
|
||||
|
||||
def dns_servers_iter(master_data):
|
||||
for m, data in master_data.items():
|
||||
if "DNS server" in data['expected_roles']['enabled']:
|
||||
yield m
|
||||
|
||||
|
||||
@pytest.fixture(params=list(enabled_role_iter(master_data)),
|
||||
ids=['role: {}, master: {}, enabled'.format(role, m)
|
||||
for m, role in enabled_role_iter(master_data)])
|
||||
def enabled_role(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=list(provided_role_iter(master_data)),
|
||||
ids=["{}: {}".format(m, ', '.join(roles)) for m, roles in
|
||||
provided_role_iter(master_data)])
|
||||
def provided_roles(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=list(configured_role_iter(master_data)),
|
||||
ids=['role: {}, master: {}, configured'.format(role, m)
|
||||
for m, role in configured_role_iter(master_data)])
|
||||
def configured_role(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=list(role_provider_iter(master_data)),
|
||||
ids=['{} providers'.format(role_name)
|
||||
for role_name, _m in
|
||||
role_provider_iter(master_data)])
|
||||
def role_providers(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=list(attribute_masters_iter(master_data)),
|
||||
ids=['{} of {}: {}'.format(attr, role, m) for m, role, attr in
|
||||
attribute_masters_iter(master_data)])
|
||||
def attribute_providers(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=list(dns_servers_iter(master_data)),
|
||||
ids=list(dns_servers_iter(master_data)))
|
||||
def dns_server(request):
|
||||
return request.param
|
||||
|
||||
|
||||
class TestServerRoleStatusRetrieval(object):
|
||||
def retrieve_role(self, master, role, mock_api, mock_masters):
|
||||
fqdn = mock_masters.get_fqdn(master)
|
||||
return mock_api.Backend.serverroles.server_role_retrieve(
|
||||
server_server=fqdn, role_servrole=role)
|
||||
|
||||
def find_role(self, role_name, mock_api, mock_masters, master=None):
|
||||
if master is not None:
|
||||
hostname = mock_masters.get_fqdn(master)
|
||||
else:
|
||||
hostname = None
|
||||
|
||||
result = mock_api.Backend.serverroles.server_role_search(
|
||||
server_server=hostname,
|
||||
role_servrole=role_name)
|
||||
|
||||
return [
|
||||
r for r in result if r[u'server_server'] not in
|
||||
mock_masters.existing_masters]
|
||||
|
||||
def get_enabled_roles_on_master(self, master, mock_api, mock_masters):
|
||||
fqdn = mock_masters.get_fqdn(master)
|
||||
result = mock_api.Backend.serverroles.server_role_search(
|
||||
server_server=fqdn, role_servrole=None, status=u'enabled'
|
||||
)
|
||||
return sorted(set(r[u'role_servrole'] for r in result))
|
||||
|
||||
def get_masters_with_enabled_role(self, role_name, mock_api, mock_masters):
|
||||
result = mock_api.Backend.serverroles.server_role_search(
|
||||
server_server=None, role_servrole=role_name)
|
||||
|
||||
return sorted(
|
||||
r[u'server_server'] for r in result if
|
||||
r[u'status'] == u'enabled' and r[u'server_server'] not in
|
||||
mock_masters.existing_masters)
|
||||
|
||||
def test_listing_of_enabled_role(
|
||||
self, mock_api, mock_masters, enabled_role):
|
||||
master, role_name = enabled_role
|
||||
result = self.retrieve_role(master, role_name, mock_api, mock_masters)
|
||||
|
||||
assert result[0][u'status'] == u'enabled'
|
||||
|
||||
def test_listing_of_configured_role(
|
||||
self, mock_api, mock_masters, configured_role):
|
||||
master, role_name = configured_role
|
||||
result = self.retrieve_role(master, role_name, mock_api, mock_masters)
|
||||
|
||||
assert result[0][u'status'] == u'configured'
|
||||
|
||||
def test_role_providers(
|
||||
self, mock_api, mock_masters, role_providers):
|
||||
role_name, providers = role_providers
|
||||
expected_masters = sorted(mock_masters.get_fqdn(m) for m in providers)
|
||||
|
||||
actual_masters = self.get_masters_with_enabled_role(
|
||||
role_name, mock_api, mock_masters)
|
||||
|
||||
assert expected_masters == actual_masters
|
||||
|
||||
def test_provided_roles_on_master(
|
||||
self, mock_api, mock_masters, provided_roles):
|
||||
master, expected_roles = provided_roles
|
||||
expected_roles.sort()
|
||||
actual_roles = self.get_enabled_roles_on_master(
|
||||
master, mock_api, mock_masters)
|
||||
assert expected_roles == actual_roles
|
||||
|
||||
def test_unknown_role_status_raises_notfound(self, mock_api, mock_masters):
|
||||
unknown_role = 'IAP maestr'
|
||||
fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster-pkinit-server')
|
||||
with pytest.raises(errors.NotFound):
|
||||
mock_api.Backend.serverroles.server_role_retrieve(
|
||||
fqdn, unknown_role)
|
||||
|
||||
def test_no_servrole_queries_all_roles_on_server(self, mock_api,
|
||||
mock_masters):
|
||||
master_name = 'ca-dns-dnssec-keymaster-pkinit-server'
|
||||
enabled_roles = master_data[master_name]['expected_roles']['enabled']
|
||||
result = self.find_role(None, mock_api, mock_masters,
|
||||
master=master_name)
|
||||
|
||||
for r in result:
|
||||
if r[u'role_servrole'] in enabled_roles:
|
||||
assert r[u'status'] == u'enabled'
|
||||
else:
|
||||
assert r[u'status'] == u'absent'
|
||||
|
||||
def test_invalid_substring_search_returns_nothing(self, mock_api,
|
||||
mock_masters):
|
||||
invalid_substr = 'fwfgbb'
|
||||
|
||||
assert (not self.find_role(invalid_substr, mock_api, mock_masters,
|
||||
'ca-dns-dnssec-keymaster-pkinit-server'))
|
||||
|
||||
|
||||
class TestServerAttributes(object):
|
||||
def config_retrieve(self, assoc_role_name, mock_api):
|
||||
return mock_api.Backend.serverroles.config_retrieve(
|
||||
assoc_role_name)
|
||||
|
||||
def config_update(self, mock_api, **attrs_values):
|
||||
return mock_api.Backend.serverroles.config_update(**attrs_values)
|
||||
|
||||
def test_attribute_master(self, mock_api, mock_masters,
|
||||
attribute_providers):
|
||||
master, assoc_role, attr_name = attribute_providers
|
||||
fqdn = mock_masters.get_fqdn(master)
|
||||
actual_attr_masters = self.config_retrieve(
|
||||
assoc_role, mock_api)[attr_name]
|
||||
|
||||
assert fqdn in actual_attr_masters
|
||||
|
||||
def test_set_attribute_on_the_same_provider_raises_emptymodlist(
|
||||
self, mock_api, mock_masters):
|
||||
attr_name = "ca_renewal_master_server"
|
||||
role_name = "CA server"
|
||||
|
||||
existing_renewal_master = self.config_retrieve(
|
||||
role_name, mock_api)[attr_name]
|
||||
|
||||
with pytest.raises(errors.EmptyModlist):
|
||||
self.config_update(
|
||||
mock_api, **{attr_name: existing_renewal_master})
|
||||
|
||||
def test_set_attribute_on_master_without_assoc_role_raises_validationerror(
|
||||
self, mock_api, mock_masters):
|
||||
attr_name = "ca_renewal_master_server"
|
||||
|
||||
non_ca_fqdn = mock_masters.get_fqdn('trust-controller-dns')
|
||||
|
||||
with pytest.raises(errors.ValidationError):
|
||||
self.config_update(mock_api, **{attr_name: non_ca_fqdn})
|
||||
|
||||
def test_set_unknown_attribute_on_master_raises_notfound(
|
||||
self, mock_api, mock_masters):
|
||||
attr_name = "ca_renuwal_maztah"
|
||||
fqdn = mock_masters.get_fqdn('trust-controller-ca')
|
||||
|
||||
with pytest.raises(errors.NotFound):
|
||||
self.config_update(mock_api, **{attr_name: [fqdn]})
|
||||
|
||||
def test_set_ca_renewal_master_on_other_ca_and_back(self, mock_api,
|
||||
mock_masters):
|
||||
attr_name = "ca_renewal_master_server"
|
||||
role_name = "CA server"
|
||||
original_renewal_master = self.config_retrieve(
|
||||
role_name, mock_api)[attr_name]
|
||||
|
||||
other_ca_server = mock_masters.get_fqdn('trust-controller-ca')
|
||||
|
||||
for host in (other_ca_server, original_renewal_master):
|
||||
self.config_update(mock_api, **{attr_name: host})
|
||||
|
||||
assert (
|
||||
self.config_retrieve(role_name, mock_api)[attr_name] == host)
|
||||
74
ipatests/test_ipaserver/test_topology_plugin.py
Normal file
74
ipatests/test_ipaserver/test_topology_plugin.py
Normal file
@@ -0,0 +1,74 @@
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
import io
|
||||
import os
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
from ipalib import api
|
||||
from ipapython.dn import DN
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.tier1
|
||||
class TestTopologyPlugin(object):
|
||||
"""
|
||||
Test Topology plugin from the DS point of view
|
||||
Testcase: http://www.freeipa.org/page/V4/Manage_replication_topology/
|
||||
Test_plan#Test_case:
|
||||
_Replication_Topology_is_listed_among_directory_server_plugins
|
||||
"""
|
||||
pwfile = os.path.join(api.env.dot_ipa, ".dmpw")
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
setup for test
|
||||
"""
|
||||
self.conn = None
|
||||
|
||||
def teardown(self):
|
||||
if self.conn and self.conn.isconnected():
|
||||
self.conn.disconnect()
|
||||
|
||||
@pytest.mark.skipif(os.path.isfile(pwfile) is False,
|
||||
reason="You did not provide a .dmpw file with the DM password")
|
||||
def test_topologyplugin(self):
|
||||
pluginattrs = {
|
||||
u'nsslapd-pluginPath': [u'libtopology'],
|
||||
u'nsslapd-pluginVendor': [u'freeipa'],
|
||||
u'cn': [u'IPA Topology Configuration'],
|
||||
u'nsslapd-plugin-depends-on-named':
|
||||
[u'Multimaster Replication Plugin', u'ldbm database'],
|
||||
u'nsslapd-topo-plugin-shared-replica-root': [u'dc=example,dc=com'],
|
||||
u'nsslapd-pluginVersion': [u'1.0'],
|
||||
u'nsslapd-topo-plugin-shared-config-base':
|
||||
[u'cn=ipa,cn=etc,dc=example,dc=com'],
|
||||
u'nsslapd-pluginDescription': [u'ipa-topology-plugin'],
|
||||
u'nsslapd-pluginEnabled': [u'on'],
|
||||
u'nsslapd-pluginId': [u'ipa-topology-plugin'],
|
||||
u'objectClass': [u'top', u'nsSlapdPlugin', u'extensibleObject'],
|
||||
u'nsslapd-topo-plugin-startup-delay': [u'20'],
|
||||
u'nsslapd-topo-plugin-shared-binddngroup':
|
||||
[u'cn=replication managers,cn=sysaccounts,cn=etc,dc=example,dc=com'],
|
||||
u'nsslapd-pluginType': [u'object'],
|
||||
u'nsslapd-pluginInitfunc': [u'ipa_topo_init']
|
||||
}
|
||||
variable_attrs = {u'nsslapd-topo-plugin-shared-replica-root',
|
||||
u'nsslapd-topo-plugin-shared-config-base',
|
||||
u'nsslapd-topo-plugin-shared-binddngroup'}
|
||||
|
||||
# Now eliminate keys that have domain-dependent values.
|
||||
checkvalues = set(pluginattrs.keys()) - variable_attrs
|
||||
topoplugindn = DN(('cn', 'IPA Topology Configuration'),
|
||||
('cn', 'plugins'),
|
||||
('cn', 'config'))
|
||||
pwfile = os.path.join(api.env.dot_ipa, ".dmpw")
|
||||
with io.open(pwfile, "r") as f:
|
||||
dm_password = f.read().rstrip()
|
||||
self.conn = ldap2(api)
|
||||
self.conn.connect(bind_dn=DN(('cn', 'directory manager')),
|
||||
bind_pw=dm_password)
|
||||
entry = self.conn.get_entry(topoplugindn)
|
||||
assert(set(entry.keys()) == set(pluginattrs.keys()))
|
||||
for i in checkvalues:
|
||||
assert(set(pluginattrs[i]) == set(entry[i]))
|
||||
51
ipatests/test_ipaserver/test_version_comparison.py
Normal file
51
ipatests/test_ipaserver/test_version_comparison.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
"""
|
||||
tests for correct RPM version comparison
|
||||
"""
|
||||
|
||||
from ipaplatform.tasks import tasks
|
||||
import pytest
|
||||
|
||||
version_strings = [
|
||||
("3.0.0-1.el6", "3.0.0-2.el6", "older"),
|
||||
("3.0.0-1.el6_8", "3.0.0-1.el6_8.1", "older"),
|
||||
("3.0.0-42.el6", "3.0.0-1.el6", "newer"),
|
||||
("3.0.0-1.el6", "3.0.0-42.el6", "older"),
|
||||
("3.0.0-42.el6", "3.3.3-1.fc20", "older"),
|
||||
("4.2.0-15.el7", "4.2.0-15.el7_2.3", "older"),
|
||||
("4.2.0-15.el7_2", "4.2.0-15.el7_2.3", "older"),
|
||||
("4.2.0-15.el7_2.3", "4.2.0-15.el7_2.3", "equal"),
|
||||
("4.2.0-15.el7_2.3", "4.2.0-15.el7_2.2", "newer"),
|
||||
("4.2.0-1.fc23", "4.2.1-1.fc23", "older"),
|
||||
("4.2.3-alpha1.fc23", "4.2.3-2.fc23", "older"), # numeric version elements
|
||||
# have precedence over
|
||||
# non-numeric ones
|
||||
("4.3.90.201601080923GIT55aeea7-0.fc23", "4.3.0-1.fc23", "newer")
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(params=version_strings)
|
||||
def versions(request):
|
||||
return request.param
|
||||
|
||||
class TestVersionComparsion(object):
|
||||
|
||||
def test_versions(self, versions):
|
||||
version_string1, version_string2, expected_comparison = versions
|
||||
|
||||
ver1 = tasks.parse_ipa_version(version_string1)
|
||||
ver2 = tasks.parse_ipa_version(version_string2)
|
||||
|
||||
if expected_comparison == "newer":
|
||||
assert ver1 > ver2
|
||||
elif expected_comparison == "older":
|
||||
assert ver1 < ver2
|
||||
elif expected_comparison == "equal":
|
||||
assert ver1 == ver2
|
||||
else:
|
||||
raise TypeError(
|
||||
"Unexpected comparison string: {}".format(expected_comparison)
|
||||
)
|
||||
Reference in New Issue
Block a user