[CalendarServer-changes] [1138] PyOpenDirectory/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Feb 6 14:10:09 PST 2007
Revision: 1138
http://trac.macosforge.org/projects/calendarserver/changeset/1138
Author: cdaboo at apple.com
Date: 2007-02-06 14:10:09 -0800 (Tue, 06 Feb 2007)
Log Message:
-----------
Merge branches/users/cdaboo/od-scheme-1044 to trunk.
Modified Paths:
--------------
PyOpenDirectory/trunk/pysrc/dsattributes.py
PyOpenDirectory/trunk/pysrc/opendirectory.py
PyOpenDirectory/trunk/src/CDirectoryService.cpp
PyOpenDirectory/trunk/src/CDirectoryService.h
PyOpenDirectory/trunk/src/CFStringUtil.cpp
PyOpenDirectory/trunk/src/PythonWrapper.cpp
PyOpenDirectory/trunk/support/test.cpp
PyOpenDirectory/trunk/test.py
Modified: PyOpenDirectory/trunk/pysrc/dsattributes.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/dsattributes.py 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/pysrc/dsattributes.py 2007-02-06 22:10:09 UTC (rev 1138)
@@ -21,6 +21,18 @@
This comes directly (with C->Python conversion) from <DirectoryServices/DirServicesConst.h>
"""
+# Specific match types
+
+eDSExact = 0x2001
+eDSStartsWith = 0x2002
+eDSEndsWith = 0x2003
+eDSContains = 0x2004
+
+eDSLessThan = 0x2005
+eDSGreaterThan = 0x2006
+eDSLessEqual = 0x2007
+eDSGreaterEqual = 0x2008
+
# Specific Record Type Constants
"""
@@ -1551,6 +1563,12 @@
kDSNAttrResourceType = "dsAttrTypeStandard:ResourceType"
"""
+ kDSNAttrServicesLocator
+ Attribute describing the services hosted for the record.
+"""
+kDSNAttrServicesLocator = "dsAttrTypeStandard:ServicesLocator"
+
+"""
kDSNAttrState
The state or province of a country.
"""
Modified: PyOpenDirectory/trunk/pysrc/opendirectory.py
===================================================================
--- PyOpenDirectory/trunk/pysrc/opendirectory.py 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/pysrc/opendirectory.py 2007-02-06 22:10:09 UTC (rev 1138)
@@ -40,6 +40,21 @@
or C{None} otherwise.
"""
+def queryRecordsWithAttributes(obj, query, matchType, casei, allmatch, recordType, attributes):
+ """
+ List records in Open Directory matching specified criteria, and return key attributes for each one.
+
+ @param obj: C{object} the object obtained from an odInit call.
+ @param query: C{dict} containing attribute/value pairs to search.
+ @param matchType: C{int} DS match type to use when searching.
+ @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+ @param allmatch: C{True} to do require all attribute/value pairs to match (AND), C{False} otherwise (OR).
+ @param recordType: C{str} containing the OD record type to lookup.
+ @param attributes: C{list} containing the attributes to return for each record.
+ @return: C{dict} containing a C{dict} of attributes for each record found,
+ or C{None} otherwise.
+ """
+
def authenticateUserBasic(obj, user, pswd):
"""
Authenticate a user with a password to Open Directory.
Modified: PyOpenDirectory/trunk/src/CDirectoryService.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.cpp 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/CDirectoryService.cpp 2007-02-06 22:10:09 UTC (rev 1138)
@@ -23,7 +23,7 @@
#include "CFStringUtil.h"
-#include <Python.h>
+#include <Python/Python.h>
#include <stdlib.h>
#include <string.h>
@@ -108,6 +108,39 @@
}
}
+// QueryRecordsWithAttributes
+//
+// Get specific attributes for one or more user records with matching attributes in the directory.
+//
+// @param query: CFDictionary of CFString listing the attributes to query.
+// @param matchType: the match type to query.
+// @param casei: true if case-insensitive match is to be used, false otherwise.
+// @param allmatch: true if a match to every attribute/value must occur (AND), false if only one needs to match (OR).
+// @param recordType: the record type to list.
+// @param attributes: CFArray of CFString listing the attributes to return for each record.
+// @return: CFMutableDictionaryRef composed of CFMutableDictionaryRef of CFStringRef key and value entries
+// for each attribute/value requested in the record indexed by uid,
+// or NULL if it fails.
+//
+CFMutableDictionaryRef CDirectoryService::QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes)
+{
+ try
+ {
+ // Get attribute map
+ return _QueryRecordsWithAttributes(query, matchType, casei, allmatch, recordType, attributes);
+ }
+ catch(long dserror)
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices Error", dserror));
+ return NULL;
+ }
+ catch(...)
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "Unknown Error", -1));
+ return NULL;
+ }
+}
+
// AuthenticateUserBasic
//
// Authenticate a user to the directory using plain text credentials.
@@ -169,6 +202,7 @@
// Get specific attributes for records of a specified type in the directory.
//
// @param type: the record type to check.
+// @param names: a list of record names to target if NULL all records are matched.
// @param attrs: a list of attributes to return.
// @return: CFMutableDictionaryRef composed of CFMutableDictionaryRef of CFStringRef key and value entries
// for each attribute/value requested in the record indexed by uid,
@@ -375,6 +409,234 @@
return result;
}
+// _QueryRecordsWithAttributes
+//
+// Get specific attributes for records of a specified type in the directory.
+//
+// @param query: a dictionary containg attribute/value pairs to match in records.
+// @param matchType: the match type to to use.
+// @param casei: true if case-insensitive match is to be used, false otherwise.
+// @param allmatch: true if a match to every attribute/value must occur (AND), false if only one needs to match (OR).
+// @param type: the record type to check.
+// @param attrs: a list of attributes to return.
+// @return: CFMutableDictionaryRef composed of CFMutableDictionaryRef of CFStringRef key and value entries
+// for each attribute/value requested in the record indexed by uid,
+// or NULL if it fails.
+//
+CFMutableDictionaryRef CDirectoryService::_QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* type, CFArrayRef attrs)
+{
+ CFMutableDictionaryRef result = NULL;
+ CFMutableDictionaryRef vresult = NULL;
+ CFMutableArrayRef values = NULL;
+ tDataNodePtr queryAttr = NULL;
+ tDataNodePtr queryValue = NULL;
+ tDataListPtr recTypes = NULL;
+ tDataListPtr attrTypes = NULL;
+ tContextData context = NULL;
+ tAttributeListRef attrListRef = 0L;
+ tRecordEntry* pRecEntry = NULL;
+
+ // Must have attributes
+ if (::CFArrayGetCount(attrs) == 0)
+ return NULL;
+
+ try
+ {
+ // Make sure we have a valid directory service
+ OpenService();
+
+ // Open the node we want to query
+ OpenNode();
+
+ // We need a buffer for what comes next
+ CreateBuffer();
+
+ // Determine attribute to search
+ if (::CFDictionaryGetCount(query) == 1)
+ {
+ BuildSimpleQuery(query, queryAttr, queryValue);
+ if (casei)
+ matchType |= 0x0100;
+ else
+ matchType &= 0xFEFF;
+ }
+ else
+ {
+ queryAttr = ::dsDataNodeAllocateString(mDir, kDS1AttrDistinguishedName);
+ BuildCompoundQuery(query, (tDirPatternMatch)matchType, allmatch, queryValue);
+ matchType = (casei) ? eDSiCompoundExpression : eDSCompoundExpression;
+ }
+
+ // Build data list of types
+ recTypes = ::dsDataListAllocate(mDir);
+ ThrowIfNULL(recTypes);
+ ThrowIfDSErr(::dsBuildListFromStringsAlloc(mDir, recTypes, type, NULL));
+
+ // Build data list of attributes
+ attrTypes = ::dsDataListAllocate(mDir);
+ ThrowIfNULL(attrTypes);
+ BuildStringDataList(attrs, attrTypes);
+
+ result = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ do
+ {
+ // List all the appropriate records
+ unsigned long recCount = 0;
+ tDirStatus err;
+ do
+ {
+ err = ::dsDoAttributeValueSearchWithData(mNode, mData, recTypes, queryAttr, (tDirPatternMatch)matchType, queryValue, attrTypes, false, &recCount, &context);
+ if (err == eDSBufferTooSmall)
+ ReallocBuffer();
+ } while(err == eDSBufferTooSmall);
+ ThrowIfDSErr(err);
+ for(unsigned long i = 1; i <= recCount; i++)
+ {
+ // Get the record entry
+ ThrowIfDSErr(::dsGetRecordEntry(mNode, mData, i, &attrListRef, &pRecEntry));
+
+ // Get the entry's name
+ char* temp = NULL;
+ ThrowIfDSErr(::dsGetRecordNameFromEntry(pRecEntry, &temp));
+ std::auto_ptr<char> recname(temp);
+
+ // Create a dictionary for the values
+ vresult = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ // Look at each requested attribute and get one value
+ for(unsigned long j = 1; j <= pRecEntry->fRecordAttributeCount; j++)
+ {
+ tAttributeValueListRef attributeValueListRef = NULL;
+ tAttributeEntryPtr attributeInfoPtr = NULL;
+
+ ThrowIfDSErr(::dsGetAttributeEntry(mNode, mData, attrListRef, j, &attributeValueListRef, &attributeInfoPtr));
+
+ if (attributeInfoPtr->fAttributeValueCount > 0)
+ {
+ // Determine what the attribute is and where in the result list it should be put
+ std::auto_ptr<char> attrname(CStringFromBuffer(&attributeInfoPtr->fAttributeSignature));
+ CFStringUtil cfattrname(attrname.get());
+
+ if (attributeInfoPtr->fAttributeValueCount > 1)
+ {
+ values = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+ for(unsigned long k = 1; k <= attributeInfoPtr->fAttributeValueCount; k++)
+ {
+ // Get the attribute value and store in results
+ tAttributeValueEntryPtr attributeValue = NULL;
+ ThrowIfDSErr(::dsGetAttributeValue(mNode, mData, k, attributeValueListRef, &attributeValue));
+ std::auto_ptr<char> data(CStringFromBuffer(&attributeValue->fAttributeValueData));
+ CFStringUtil strvalue(data.get());
+ ::CFArrayAppendValue(values, strvalue.get());
+ ::dsDeallocAttributeValueEntry(mDir, attributeValue);
+ attributeValue = NULL;
+ }
+ ::CFDictionarySetValue(vresult, cfattrname.get(), values);
+ ::CFRelease(values);
+ values = NULL;
+ }
+ else
+ {
+ // Get the attribute value and store in results
+ tAttributeValueEntryPtr attributeValue = NULL;
+ ThrowIfDSErr(::dsGetAttributeValue(mNode, mData, 1, attributeValueListRef, &attributeValue));
+ std::auto_ptr<char> data(CStringFromBuffer(&attributeValue->fAttributeValueData));
+ CFStringUtil strvalue(data.get());
+ ::CFDictionarySetValue(vresult, cfattrname.get(), strvalue.get());
+ ::dsDeallocAttributeValueEntry(mDir, attributeValue);
+ attributeValue = NULL;
+ }
+ }
+
+ ::dsCloseAttributeValueList(attributeValueListRef);
+ attributeValueListRef = NULL;
+ ::dsDeallocAttributeEntry(mDir, attributeInfoPtr);
+ attributeInfoPtr = NULL;
+ }
+
+ // Add dictionary of values to result array
+ CFStringUtil str(recname.get());
+ ::CFDictionarySetValue(result, str.get(), vresult);
+ ::CFRelease(vresult);
+ vresult = NULL;
+
+ // Clean-up
+ ::dsCloseAttributeList(attrListRef);
+ attrListRef = 0L;
+ ::dsDeallocRecordEntry(mDir, pRecEntry);
+ pRecEntry = NULL;
+ }
+ } while (context != NULL); // Loop until all data has been obtained.
+
+ // Cleanup
+ ::dsDataListDeallocate(mDir, recTypes);
+ ::dsDataListDeallocate(mDir, attrTypes);
+ ::dsDataNodeDeAllocate(mDir, queryValue);
+ ::dsDataNodeDeAllocate(mDir, queryAttr);
+ free(recTypes);
+ free(attrTypes);
+ RemoveBuffer();
+ CloseNode();
+ CloseService();
+ }
+ catch(long dsStatus)
+ {
+ // Cleanup
+ if (context != NULL)
+ ::dsReleaseContinueData(mDir, context);
+ if (attrListRef != 0L)
+ ::dsCloseAttributeList(attrListRef);
+ if (pRecEntry != NULL)
+ dsDeallocRecordEntry(mDir, pRecEntry);
+ if (recTypes != NULL)
+ {
+ ::dsDataListDeallocate(mDir, recTypes);
+ free(recTypes);
+ recTypes = NULL;
+ }
+ if (attrTypes != NULL)
+ {
+ ::dsDataListDeallocate(mDir, attrTypes);
+ free(attrTypes);
+ attrTypes = NULL;
+ }
+ if (queryValue != NULL)
+ {
+ ::dsDataNodeDeAllocate(mDir, queryValue);
+ queryValue = NULL;
+ }
+ if (queryAttr != NULL)
+ {
+ ::dsDataNodeDeAllocate(mDir, queryAttr);
+ queryAttr = NULL;
+ }
+ RemoveBuffer();
+ CloseNode();
+ CloseService();
+
+ if (values != NULL)
+ {
+ ::CFRelease(values);
+ values = NULL;
+ }
+ if (vresult != NULL)
+ {
+ ::CFRelease(vresult);
+ vresult = NULL;
+ }
+ if (result != NULL)
+ {
+ ::CFRelease(result);
+ result = NULL;
+ }
+ throw;
+ }
+
+ return result;
+}
+
// AuthenticationGetNode
//
// Authenticate a user to the directory.
@@ -846,6 +1108,106 @@
}
}
+struct SBuildSimpleQueryIterator
+{
+ tDirReference mDir;
+ tDataNodePtr& mAttr;
+ tDataNodePtr& mValue;
+
+ SBuildSimpleQueryIterator(tDirReference dir, tDataNodePtr& attr, tDataNodePtr& value) :
+ mDir(dir), mAttr(attr), mValue(value) {}
+};
+
+// CFDictionary iterator callback that adds either the key or value to a DS list.
+static void BuildSimpleQueryIterator(const void* key, const void* value, void* ref)
+{
+ SBuildSimpleQueryIterator* info = (SBuildSimpleQueryIterator*)ref;
+
+ CFStringRef strkey = (CFStringRef)key;
+ CFStringUtil cstrkey(strkey);
+ info->mAttr = ::dsDataNodeAllocateString(info->mDir, cstrkey.temp_str());
+ ThrowIfNULL(info->mAttr);
+
+ CFStringRef strvalue = (CFStringRef)value;
+ CFStringUtil cstrvalue(strvalue);
+ info->mValue = ::dsDataNodeAllocateString(info->mDir, cstrvalue.temp_str());
+ ThrowIfNULL(info->mValue);
+}
+
+void CDirectoryService::BuildSimpleQuery(CFDictionaryRef dict, tDataNodePtr& attr, tDataNodePtr& value)
+{
+ SBuildSimpleQueryIterator info(mDir, attr, value);
+
+ ::CFDictionaryApplyFunction(dict, BuildSimpleQueryIterator, &info);
+}
+
+struct SBuildCompoundQueryIterator
+{
+ CFStringRef mFormat;
+ CFMutableStringRef mCompound;
+
+ SBuildCompoundQueryIterator(CFStringRef format, CFMutableStringRef compound) :
+ mFormat(format), mCompound(compound) {}
+};
+
+// CFDictionary iterator callback that adds either the key or value to a DS list.
+static void BuildCompoundQueryIterator(const void* key, const void* value, void* ref)
+{
+ SBuildCompoundQueryIterator* info = (SBuildCompoundQueryIterator*)ref;
+
+ CFStringRef strkey = (CFStringRef)key;
+ CFStringRef strvalue = (CFStringRef)value;
+ ::CFStringAppendFormat(info->mCompound, NULL, info->mFormat, strkey, strvalue);
+}
+
+void CDirectoryService::BuildCompoundQuery(CFDictionaryRef dict, tDirPatternMatch matchType, bool allmatch, tDataNodePtr& value)
+{
+ CFStringRef format = NULL;
+ switch(matchType)
+ {
+ case eDSExact:
+ case eDSiExact:
+ format = CFSTR("(%@=%@)");
+ break;
+ case eDSStartsWith:
+ case eDSiStartsWith:
+ format = CFSTR("(%@=%@*)");
+ break;
+ case eDSEndsWith:
+ case eDSiEndsWith:
+ format = CFSTR("(%@=*%@)");
+ break;
+ case eDSContains:
+ case eDSiContains:
+ format = CFSTR("(%@=*%@*)");
+ break;
+ case eDSLessThan:
+ case eDSiLessThan:
+ format = CFSTR("(%@<%@)");
+ break;
+ case eDSGreaterThan:
+ case eDSiGreaterThan:
+ format = CFSTR("(%@>%@)");
+ break;
+ default:
+ format = CFSTR("(%@=*%@*)");
+ break;
+ }
+
+ // Now build the compound expression using the match type format we just setup
+ CFMutableStringRef compound = CFStringCreateMutable(kCFAllocatorDefault, 0);
+ ::CFStringAppend(compound, allmatch ? CFSTR("(&") : CFSTR("(|"));
+
+ SBuildCompoundQueryIterator info(format, compound);
+ ::CFDictionaryApplyFunction(dict, BuildCompoundQueryIterator, &info);
+
+ ::CFStringAppend(compound, CFSTR(")"));
+ CFStringUtil cstrvalue(compound);
+ value = ::dsDataNodeAllocateString(mDir, cstrvalue.temp_str());
+ ::CFRelease(compound);
+ ThrowIfNULL(value);
+}
+
// CStringFromBuffer
//
// Convert data in a buffer into a c-string.
Modified: PyOpenDirectory/trunk/src/CDirectoryService.h
===================================================================
--- PyOpenDirectory/trunk/src/CDirectoryService.h 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/CDirectoryService.h 2007-02-06 22:10:09 UTC (rev 1138)
@@ -33,6 +33,7 @@
~CDirectoryService();
CFMutableDictionaryRef ListAllRecordsWithAttributes(const char* recordType, CFArrayRef attributes);
+ CFMutableDictionaryRef QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes);
bool AuthenticateUserBasic(const char* user, const char* pswd, bool& result);
bool AuthenticateUserDigest(const char* user, const char* challenge, const char* response, const char* method, bool& result);
@@ -45,6 +46,7 @@
UInt32 mDataSize;
CFMutableDictionaryRef _ListAllRecordsWithAttributes(const char* type, CFArrayRef names, CFArrayRef attrs);
+ CFMutableDictionaryRef _QueryRecordsWithAttributes(CFDictionaryRef query, int matchType, bool casei, bool allmatch, const char* recordType, CFArrayRef attributes);
CFStringRef CDirectoryService::AuthenticationGetNode(const char* user);
bool NativeAuthenticationBasic(const char* user, const char* pswd);
@@ -64,6 +66,9 @@
void ReallocBuffer();
void BuildStringDataList(CFArrayRef strs, tDataListPtr data);
+
+ void BuildSimpleQuery(CFDictionaryRef dict, tDataNodePtr& attr, tDataNodePtr& value);
+ void BuildCompoundQuery(CFDictionaryRef dict, tDirPatternMatch matchType, bool allmatch, tDataNodePtr& value);
char* CStringFromBuffer(tDataBufferPtr data);
char* CStringFromData(const char* data, size_t len);
Modified: PyOpenDirectory/trunk/src/CFStringUtil.cpp
===================================================================
--- PyOpenDirectory/trunk/src/CFStringUtil.cpp 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/CFStringUtil.cpp 2007-02-06 22:10:09 UTC (rev 1138)
@@ -77,13 +77,39 @@
if (bytes == NULL)
{
- char localBuffer[256];
- localBuffer[0] = 0;
- Boolean success = ::CFStringGetCString(mRef, localBuffer, 256, kCFStringEncodingUTF8);
- if (!success)
- localBuffer[0] = 0;
+ // Need to convert the CFString to UTF-8. Since we don't know the exact length of the UTF-8 data
+ // we have to iterate over the conversion process until we succeed or the length we are allocating
+ // is greater than the value it could maximally be. We start with half the UTF-16 encoded value,
+ // which will give an accurate count for an ascii only string (plus add one for \0).
+ CFIndex len = ::CFStringGetLength(mRef)/2 + 1;
+ CFIndex maxSize = ::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(mRef), kCFStringEncodingUTF8) + 1;
+ char* buffer = NULL;
+ while(true)
+ {
+ buffer = (char*)::malloc(len);
+ if (buffer == NULL)
+ break;
+ buffer[0] = 0;
+ Boolean success = ::CFStringGetCString(mRef, buffer, len, kCFStringEncodingUTF8);
+ if (!success)
+ {
+ ::free(buffer);
+ buffer = NULL;
+ if (len == maxSize)
+ {
+ buffer = (char*)::malloc(1);
+ buffer[0] = 0;
+ break;
+ }
+ len *= 2;
+ if (len > maxSize)
+ len = maxSize;
+ }
+ else
+ break;
+ }
- return ::strdup(localBuffer);
+ return buffer;
}
else
{
Modified: PyOpenDirectory/trunk/src/PythonWrapper.cpp
===================================================================
--- PyOpenDirectory/trunk/src/PythonWrapper.cpp 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/src/PythonWrapper.cpp 2007-02-06 22:10:09 UTC (rev 1138)
@@ -35,23 +35,8 @@
// Utility function - not exposed to Python
static PyObject* CFStringToPyStr(CFStringRef str)
{
- PyObject* pystr = NULL;
- const char* bytes = CFStringGetCStringPtr(str, kCFStringEncodingUTF8);
-
- if (bytes == NULL)
- {
- char localBuffer[256];
- Boolean success = CFStringGetCString(str, localBuffer, 256, kCFStringEncodingUTF8);
- if (!success)
- localBuffer[0] = 0;
- pystr = PyString_FromString(localBuffer);
- }
- else
- {
- pystr = PyString_FromString(bytes);
- }
-
- return pystr;
+ CFStringUtil s(str);
+ return PyString_FromString(s.temp_str());
}
// Utility function - not exposed to Python
@@ -345,6 +330,83 @@
}
/*
+def queryRecordsWithAttributes(obj, query, matchType, casei, allmatch, recordType, attributes):
+ """
+ List records in Open Directory matching specified criteria, and return key attributes for each one.
+
+ @param obj: C{object} the object obtained from an odInit call.
+ @param query: C{dict} containing attribute/value pairs to search.
+ @param matchType: C{int} DS match type to use when searching.
+ @param casei: C{True} to do case-insenstive match, C{False} otherwise.
+ @param allmatch: C{True} to do require all attribute/value pairs to match (AND), C{False} otherwise (OR).
+ @param recordType: C{str} containing the OD record type to lookup.
+ @param attributes: C{list} containing the attributes to return for each record.
+ @return: C{dict} containing a C{dict} of attributes for each record found,
+ or C{None} otherwise.
+ """
+ */
+extern "C" PyObject *queryRecordsWithAttributes(PyObject *self, PyObject *args)
+{
+ PyObject* pyds;
+ PyObject* query;
+ int matchType;
+ PyObject* caseio;
+ bool casei;
+ PyObject* allmatcho;
+ bool allmatch;
+ const char* recordType;
+ PyObject* attributes;
+ if (!PyArg_ParseTuple(args, "OOiOOsO", &pyds, &query, &matchType, &caseio, &allmatcho, &recordType, &attributes) ||
+ !PyCObject_Check(pyds) || !PyDict_Check(query) || !PyBool_Check(caseio) || !PyBool_Check(allmatcho) || !PyList_Check(attributes))
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse arguments", 0));
+ return NULL;
+ }
+
+ // Convert dict to CFDictionary of CFString
+ CFDictionaryRef cfquery = PyDictToCFDictionary(query);
+ if (cfquery == NULL)
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse query dict", 0));
+ return NULL;
+ }
+
+ casei = (caseio == Py_True);
+
+ allmatch = (allmatcho == Py_True);
+
+ // Convert list to CFArray of CFString
+ CFArrayRef cfattributes = PyListToCFArray(attributes);
+ if (cfattributes == NULL)
+ {
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: could not parse attributes list", 0));
+ CFRelease(cfquery);
+ return NULL;
+ }
+
+ CDirectoryService* ds = static_cast<CDirectoryService*>(PyCObject_AsVoidPtr(pyds));
+ if (ds != NULL)
+ {
+ CFMutableDictionaryRef dict = ds->QueryRecordsWithAttributes(cfquery, matchType, casei, allmatch, recordType, cfattributes);
+ if (dict != NULL)
+ {
+ PyObject* result = CFDictionaryDictionaryToPyDict(dict);
+ CFRelease(dict);
+ CFRelease(cfquery);
+ CFRelease(cfattributes);
+
+ return result;
+ }
+ }
+ else
+ PyErr_SetObject(ODException_class, Py_BuildValue("((s:i))", "DirectoryServices queryRecordsWithAttributes: invalid directory service argument", 0));
+
+ CFRelease(cfquery);
+ CFRelease(cfattributes);
+ return NULL;
+}
+
+/*
def authenticateUserBasic(obj, user, pswd):
"""
Authenticate a user with a password to Open Directory.
@@ -433,6 +495,8 @@
"Initialize the Open Directory system."},
{"listAllRecordsWithAttributes", listAllRecordsWithAttributes, METH_VARARGS,
"List all records of the specified type in Open Directory, returning requested attributes."},
+ {"queryRecordsWithAttributes", queryRecordsWithAttributes, METH_VARARGS,
+ "List records in Open Directory matching specified criteria, and return key attributes for each one."},
{"authenticateUserBasic", authenticateUserBasic, METH_VARARGS,
"Authenticate a user with a password to Open Directory using plain text authentication."},
{"authenticateUserDigest", authenticateUserDigest, METH_VARARGS,
Modified: PyOpenDirectory/trunk/support/test.cpp
===================================================================
--- PyOpenDirectory/trunk/support/test.cpp 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/support/test.cpp 2007-02-06 22:10:09 UTC (rev 1138)
@@ -34,11 +34,13 @@
void PrintArrayArray(CFMutableArrayRef list);
void PrintArray(CFArrayRef list);
void AuthenticateUser(CDirectoryService* dir, const char* user, const char* pswd);
+void AuthenticateUserDigest(CDirectoryService* dir, const char* user, const char* challenge, const char* response, const char* method);
int main (int argc, const char * argv[]) {
CDirectoryService* dir = new CDirectoryService("/Search");
+#if 0
CFStringRef strings[2];
strings[0] = CFSTR(kDS1AttrDistinguishedName);
strings[1] = CFSTR(kDS1AttrGeneratedUID);
@@ -74,20 +76,89 @@
}
CFRelease(array);
- AuthenticateUser(dir, "cdaboo", "appledav1234");
- AuthenticateUser(dir, "cdaboo", "appledav6585");
+ AuthenticateUser(dir, "test", "test-no");
+ AuthenticateUser(dir, "test", "test-yes");
+#elif 0
+ CFStringRef keys[2];
+ keys[0] = CFSTR(kDS1AttrFirstName);
+ keys[1] = CFSTR(kDS1AttrLastName);
+ CFStringRef values[2];
+ values[0] = CFSTR("cyrus");
+ values[1] = CFSTR("daboo");
+ CFDictionaryRef kvdict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void**)values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFStringRef strings[2];
+ strings[0] = CFSTR(kDS1AttrDistinguishedName);
+ strings[1] = CFSTR(kDS1AttrGeneratedUID);
+ CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
+
+ CFMutableDictionaryRef dict = dir->QueryRecordsWithAttributes(kvdict, eDSContains, false, false, kDSStdRecordTypeUsers, array);
+ if (dict != NULL)
+ {
+ printf("\n*** Users: %d ***\n", CFDictionaryGetCount(dict));
+ CFDictionaryApplyFunction(dict, PrintDictionaryDictionary, NULL);
+ CFRelease(dict);
+ }
+ else
+ {
+ printf("\nNo Users returned\n");
+ }
+ CFRelease(array);
+#elif 1
+ CFStringRef keys[2];
+ keys[0] = CFSTR(kDS1AttrENetAddress);
+ CFStringRef values[2];
+ values[0] = CFSTR("00:17:f2:02:35:e4");
+ CFDictionaryRef kvdict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void**)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ CFStringRef strings[2];
+ strings[0] = CFSTR(kDS1AttrDistinguishedName);
+ strings[1] = CFSTR(kDS1AttrXMLPlist);
+ CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void **)strings, 2, &kCFTypeArrayCallBacks);
+
+ CFMutableDictionaryRef dict = dir->QueryRecordsWithAttributes(kvdict, eDSExact, false, false, kDSStdRecordTypeComputers, array);
+ if (dict != NULL)
+ {
+ printf("\n*** Computers: %d ***\n", CFDictionaryGetCount(dict));
+ CFDictionaryApplyFunction(dict, PrintDictionaryDictionary, NULL);
+ CFRelease(dict);
+ }
+ else
+ {
+ printf("\nNo Users returned\n");
+ }
+ CFRelease(array);
+
+#else
+ const char* u = "test";
+ //const char* c = "nonce=\"1\", qop=\"auth\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\"";
+ //const char* r = "username=\"test\", nonce=\"1\", cnonce=\"1\", nc=\"1\", realm=\"Test\", algorithm=\"md5\", opaque=\"1\", qop=\"auth\", uri=\"/\", response=\"4241f31ffe6f9c99b891f88e9c41caa9\"";
+ const char* c = "WWW-Authenticate: digest nonce=\"1621696297327727918745238639\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", realm=\"Test Realm\", algorithm=\"md5\", qop=\"auth\"";
+ const char* r = "Authorization: Digest username=\"test\", realm=\"Test Realm\", nonce=\"1621696297327727918745238639\", uri=\"/principals/users/test/\", response=\"e260f13cffcc15572ddeec9c31de437b\", opaque=\"121994e78694cbdff74f12cb32ee6f00-MTYyMTY5NjI5NzMyNzcyNzkxODc0NTIzODYzOSwxMjcuMC4wLjEsMTE2ODU2ODg5NQ==\", algorithm=\"md5\", cnonce=\"70cbd8f04227d8d46c0193b290beaf0d\", nc=00000001, qop=\"auth\"";
+ AuthenticateUserDigest(dir, u, c, r, "GET");
+#endif
return 0;
}
void AuthenticateUser(CDirectoryService* dir, const char* user, const char* pswd)
{
- if (dir->AuthenticateUserBasic(user, pswd))
+ bool result = false;
+ if (dir->AuthenticateUserBasic(user, pswd, result))
printf("Authenticated user: %s\n", user);
else
printf("Not Authenticated user: %s\n", user);
}
+void AuthenticateUserDigest(CDirectoryService* dir, const char* user, const char* challenge, const char* response, const char* method)
+{
+ bool result = false;
+ if (dir->AuthenticateUserDigest(user, challenge, response, method, result))
+ printf("Authenticated user: %s\n", user);
+ else
+ printf("Not Authenticated user: %s\n", user);
+}
+
void CFDictionaryIterator(const void* key, const void* value, void* ref)
{
CFStringRef strkey = (CFStringRef)key;
Modified: PyOpenDirectory/trunk/test.py
===================================================================
--- PyOpenDirectory/trunk/test.py 2007-02-06 21:53:59 UTC (rev 1137)
+++ PyOpenDirectory/trunk/test.py 2007-02-06 22:10:09 UTC (rev 1138)
@@ -26,34 +26,125 @@
print "Failed odInit"
else:
print "OK odInit"
+
+ def listUsers():
+ d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,])
+ if d is None:
+ print "Failed to list users"
+ else:
+ names = [v for v in d.iterkeys()]
+ names.sort()
+ print "\nlistUsers number of results = %d" % (len(names),)
+ for n in names:
+ print "Name: %s" % n
+ print "dict: %s" % str(d[n])
- d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeUsers,
- [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,])
- if d is None:
- print "Failed to list users"
- else:
- names = [v for v in d.iterkeys()]
- names.sort()
- for n in names:
- print "Name: %s" % n
- print "dict: %s" % str(d[n])
+ def listGroups():
+ d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeGroups,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDSNAttrGroupMembers,])
+ if d is None:
+ print "Failed to list groups"
+ else:
+ names = [v for v in d.iterkeys()]
+ names.sort()
+ print "\nlistGroups number of results = %d" % (len(names),)
+ for n in names:
+ print "Name: %s" % n
+ print "dict: %s" % str(d[n])
- d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeGroups,
- [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDSNAttrGroupMembers,])
- if d is None:
- print "Failed to list groups"
- else:
- names = [v for v in d.iterkeys()]
- names.sort()
- for n in names:
- print "Name: %s" % n
- print "dict: %s" % str(d[n])
+ def listComputers():
+ d = opendirectory.listAllRecordsWithAttributes(ref, dsattributes.kDSStdRecordTypeComputers,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrXMLPlist,])
+ if d is None:
+ print "Failed to list computers"
+ else:
+ names = [v for v in d.iterkeys()]
+ names.sort()
+ print "\nlistComputers number of results = %d" % (len(names),)
+ for n in names:
+ print "Name: %s" % n
+ print "dict: %s" % str(d[n])
- if opendirectory.authenticateUserBasic(ref, "test", "test"):
- print "Authenticated user"
- else:
- print "Failed to authenticate user"
+ def query(title, dict, matchType, casei, allmatch, recordType, attrs):
+ d = opendirectory.queryRecordsWithAttributes(
+ ref,
+ dict,
+ matchType,
+ casei,
+ allmatch,
+ recordType,
+ attrs
+ )
+ if d is None:
+ print "Failed to query users"
+ else:
+ names = [v for v in d.iterkeys()]
+ names.sort()
+ print "\n%s number of results = %d" % (title, len(names),)
+ for n in names:
+ print "Name: %s" % n
+ print "dict: %s" % str(d[n])
+
+ def queryUsers():
+ query(
+ "queryUsers",
+ {dsattributes.kDS1AttrFirstName: "cyrus",},
+ dsattributes.eDSExact,
+ True,
+ True,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+ )
+
+ def queryUsersCompoundOr():
+ query(
+ "queryUsersCompoundOr",
+ {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
+ dsattributes.eDSContains,
+ True,
+ False,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+ )
+
+ def queryUsersCompoundOrExact():
+ query(
+ "queryUsersCompoundOrExact",
+ {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
+ dsattributes.eDSExact,
+ True,
+ False,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+ )
+
+ def queryUsersCompoundAnd():
+ query(
+ "queryUsersCompoundAnd",
+ {dsattributes.kDS1AttrFirstName: "chris", dsattributes.kDS1AttrLastName: "roy",},
+ dsattributes.eDSContains,
+ True,
+ True,
+ dsattributes.kDSStdRecordTypeUsers,
+ [dsattributes.kDS1AttrGeneratedUID, dsattributes.kDS1AttrDistinguishedName,]
+ )
+
+ def authentciateBasic():
+ if opendirectory.authenticateUserBasic(ref, "test", "test"):
+ print "Authenticated user"
+ else:
+ print "Failed to authenticate user"
+ #listUsers()
+ #listGroups()
+ listComputers()
+ #queryUsers()
+ #queryUsersCompoundOr()
+ #queryUsersCompoundOrExact()
+ #queryUsersCompoundAnd()
+ #authentciateBasic()
+
ref = None
except opendirectory.ODError, ex:
print ex
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070206/8fb7953b/attachment.html
More information about the calendarserver-changes
mailing list