1/*
2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25#ifndef SEC_SOSAccountTesting_h
26#define SEC_SOSAccountTesting_h
27
28#include <CoreFoundation/CoreFoundation.h>
29#include <SecureObjectSync/SOSAccount.h>
30#include <SecureObjectSync/SOSAccountPriv.h>
31#include <SecureObjectSync/SOSTransport.h>
32#include "SOSTransportTestTransports.h"
33//
34// Account comparison
35//
36
37#define kAccountsAgreeTestMin 9
38#define kAccountsAgreeTestPerPeer 1
39#define accountsAgree(x) (kAccountsAgreeTestMin + kAccountsAgreeTestPerPeer * (x))
40
41
42static SOSAccountRef SOSAccountCreateBasicTest(CFAllocatorRef allocator,
43                                               CFStringRef accountName,
44                                               CFDictionaryRef gestalt,
45                                               SOSDataSourceFactoryRef factory) {
46    SOSAccountRef a = SOSAccountCreateBasic(allocator, gestalt, factory);
47
48    return a;
49}
50
51static SOSAccountRef SOSAccountCreateTest(CFAllocatorRef allocator,
52                                          CFStringRef accountName,
53                                          CFDictionaryRef gestalt,
54                                          SOSDataSourceFactoryRef factory) {
55    SOSAccountRef a = SOSAccountCreateBasicTest(allocator, accountName, gestalt, factory);
56
57    a->retired_peers = CFDictionaryCreateMutableForCFTypes(allocator);
58    SOSUnregisterTransportKeyParameter(a->key_transport);
59
60    CFReleaseNull(a->circle_transports);
61    CFReleaseNull(a->message_transports);
62    CFReleaseNull(a->key_transport);
63
64    SOSAccountEnsureFactoryCirclesTest(a, accountName);
65
66    return a;
67}
68
69
70static void unretired_peers_is_subset(const char* label, CFArrayRef peers, CFArrayRef allowed_peers)
71{
72    CFArrayForEach(peers, ^(const void *value) {
73        SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
74        CFErrorRef leftError = NULL;
75        CFErrorRef rightError = NULL;
76
77        ok(SOSPeerInfoIsRetirementTicket(pi) || SOSPeerInfoIsCloudIdentity(pi) || CFArrayContainsValue(allowed_peers, CFRangeMake(0, CFArrayGetCount(allowed_peers)), pi), "Peer is allowed (%s) Peer: %@, Allowed %@", label, pi, allowed_peers);
78
79        CFReleaseNull(leftError);
80        CFReleaseNull(rightError);
81    });
82}
83
84static void accounts_agree_internal(char *label, SOSAccountRef left, SOSAccountRef right, bool check_peers)
85{
86    CFErrorRef error = NULL;
87    {
88        CFArrayRef leftPeers = SOSAccountCopyActivePeers(left, &error);
89        ok(leftPeers, "Left peers (%@) - %s", error, label);
90        CFReleaseNull(error);
91
92        CFArrayRef rightPeers = SOSAccountCopyActivePeers(right, &error);
93        ok(rightPeers, "Right peers (%@) - %s", error, label);
94        CFReleaseNull(error);
95
96        ok(CFEqual(leftPeers, rightPeers), "Matching peers (%s) Left: %@, Right: %@", label, leftPeers, rightPeers);
97
98        if (check_peers) {
99            CFMutableArrayRef allowed_identities = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
100
101            CFArrayRef leftIdentities = SOSAccountCopyAccountIdentityPeerInfos(left, kCFAllocatorDefault, &error);
102            ok(leftIdentities, "Get identities (%@)", error);
103            CFReleaseNull(error);
104
105            CFArrayAppendArray(allowed_identities, leftIdentities, CFRangeMake(0, CFArrayGetCount(leftIdentities)));
106
107            CFReleaseNull(leftIdentities);
108
109            CFArrayRef rightIdentities = SOSAccountCopyAccountIdentityPeerInfos(right, kCFAllocatorDefault, &error);
110            ok(rightIdentities, "Get identities (%@)", error);
111            CFReleaseNull(error);
112
113            CFArrayAppendArray(allowed_identities, rightIdentities, CFRangeMake(0, CFArrayGetCount(rightIdentities)));
114
115            CFReleaseNull(rightIdentities);
116
117            unretired_peers_is_subset(label, leftPeers, allowed_identities);
118        }
119
120        CFReleaseNull(leftPeers);
121        CFReleaseNull(rightPeers);
122    }
123    {
124        CFArrayRef leftConcurringPeers = SOSAccountCopyConcurringPeers(left, &error);
125        ok(leftConcurringPeers, "Left peers (%@) - %s", error, label);
126
127        CFArrayRef rightConcurringPeers = SOSAccountCopyConcurringPeers(right, &error);
128        ok(rightConcurringPeers, "Right peers (%@) - %s", error, label);
129
130        ok(CFEqual(leftConcurringPeers, rightConcurringPeers), "Matching concurring peers Left: %@, Right: %@", leftConcurringPeers, rightConcurringPeers);
131
132        CFReleaseNull(leftConcurringPeers);
133        CFReleaseNull(rightConcurringPeers);
134    }
135    {
136        CFArrayRef leftApplicants = SOSAccountCopyApplicants(left, &error);
137        ok(leftApplicants, "Left Applicants (%@) - %s", error, label);
138
139        CFArrayRef rightApplicants = SOSAccountCopyApplicants(right, &error);
140        ok(rightApplicants, "Left Applicants (%@) - %s", error, label);
141
142        ok(CFEqual(leftApplicants, rightApplicants), "Matching applicants (%s) Left: %@, Right: %@", label, leftApplicants, rightApplicants);
143
144        CFReleaseNull(leftApplicants);
145        CFReleaseNull(rightApplicants);
146    }
147}
148
149static inline void accounts_agree(char *label, SOSAccountRef left, SOSAccountRef right)
150{
151    accounts_agree_internal(label, left, right, true);
152}
153
154
155//
156// Change handling
157//
158
159static void CFDictionaryOverlayDictionary(CFMutableDictionaryRef target, CFDictionaryRef overlay) {
160    CFDictionaryForEach(overlay, ^(const void *key, const void *value) {
161        CFDictionarySetValue(target, key, value);
162    });
163}
164
165static void CFArrayAppendKeys(CFMutableArrayRef keys, CFDictionaryRef newKeysToAdd) {
166    CFDictionaryForEach(newKeysToAdd, ^(const void *key, const void *value) {
167        CFArrayAppendValue(keys, key);
168    });
169}
170
171static bool AddNewChanges(CFMutableDictionaryRef changesRecord, CFMutableDictionaryRef newKeysAndValues, SOSAccountRef sender)
172{
173    __block bool changes_added = false;
174    CFMutableDictionaryRef emptyDictionary = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
175    CFDictionaryAddValue(changesRecord, kCFNull, emptyDictionary);
176    CFReleaseNull(emptyDictionary);
177
178    CFDictionaryOverlayDictionary((CFMutableDictionaryRef) CFDictionaryGetValue(changesRecord, kCFNull), newKeysAndValues);
179
180    CFDictionaryForEach(changesRecord, ^(const void *key, const void *value) {
181        if (isArray(value) && (sender == NULL || !CFEqual(sender, key))) {
182            CFArrayAppendKeys((CFMutableArrayRef) value, newKeysAndValues);
183            if (CFDictionaryGetCount(newKeysAndValues))
184                changes_added = true;
185        }
186    });
187
188    CFDictionaryRemoveAllValues(newKeysAndValues);
189
190    return changes_added;
191}
192
193static bool FillAllChanges(CFMutableDictionaryRef changes) {
194    __block bool changed = false;
195    CFArrayForEach(key_transports, ^(const void *value) {
196        SOSTransportKeyParameterTestRef tpt = (SOSTransportKeyParameterTestRef) value;
197        changed |= AddNewChanges(changes, SOSTransportKeyParameterTestGetChanges(tpt), SOSTransportKeyParameterTestGetAccount(tpt));
198    });
199    CFArrayForEach(circle_transports, ^(const void *value) {
200        SOSTransportCircleTestRef tpt = (SOSTransportCircleTestRef) value;
201        changed |= AddNewChanges(changes, SOSTransportCircleTestGetChanges(tpt), SOSTransportCircleTestGetAccount(tpt));
202    });
203    CFArrayForEach(message_transports, ^(const void *value) {
204        SOSTransportMessageTestRef tpt = (SOSTransportMessageTestRef) value;
205        CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt), kCFNull);
206        changed |=AddNewChanges(changes, SOSTransportMessageTestGetChanges(tpt), SOSTransportMessageTestGetAccount(tpt));
207    });
208
209    return changed;
210}
211
212static void FillChanges(CFMutableDictionaryRef changes, SOSAccountRef forAccount)
213{
214    CFArrayForEach(key_transports, ^(const void *value) {
215        SOSTransportKeyParameterTestRef tpt = (SOSTransportKeyParameterTestRef) value;
216        if(CFEqualSafe(forAccount, SOSTransportKeyParameterTestGetAccount(tpt))){
217            AddNewChanges(changes, SOSTransportKeyParameterTestGetChanges(tpt), SOSTransportKeyParameterTestGetAccount(tpt));
218        }
219    });
220    CFArrayForEach(circle_transports, ^(const void *value) {
221        SOSTransportCircleTestRef tpt = (SOSTransportCircleTestRef) value;
222        if(CFEqualSafe(forAccount, SOSTransportCircleTestGetAccount(tpt))){
223            AddNewChanges(changes, SOSTransportCircleTestGetChanges(tpt), SOSTransportCircleTestGetAccount(tpt));
224        }
225    });
226    CFArrayForEach(message_transports, ^(const void *value) {
227        SOSTransportMessageTestRef tpt = (SOSTransportMessageTestRef) value;
228        if(CFEqualSafe(forAccount, SOSTransportMessageTestGetAccount(tpt))){
229            CFDictionaryRemoveValue(SOSTransportMessageTestGetChanges(tpt), kCFNull);
230            AddNewChanges(changes, SOSTransportMessageTestGetChanges(tpt), SOSTransportMessageTestGetAccount(tpt));
231        }
232    });
233
234}
235
236static inline void FillChangesMulti(CFMutableDictionaryRef changes, SOSAccountRef account, ...)
237{
238    SOSAccountRef next_account = account;
239    va_list argp;
240    va_start(argp, account);
241    while(next_account != NULL) {
242        FillChanges(changes, next_account);
243        next_account = va_arg(argp, SOSAccountRef);
244    }
245}
246
247static inline CFMutableArrayRef CFDictionaryCopyKeys(CFDictionaryRef dictionary)
248{
249    CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
250
251    CFArrayAppendKeys(result, dictionary);
252
253    return result;
254}
255
256#define kFeedChangesToTestCount 1
257static inline void FeedChangesTo(CFMutableDictionaryRef changes, SOSAccountRef account)
258{
259    CFDictionaryRef full_list = (CFDictionaryRef) CFDictionaryGetValue(changes, kCFNull);
260
261    if (!isDictionary(full_list))
262        return; // Nothing recorded to send!
263
264    CFMutableArrayRef account_pending_keys = (CFMutableArrayRef)CFDictionaryGetValue(changes, account);
265    if (!isArray(account_pending_keys)) {
266        CFReleaseNull(account_pending_keys);
267
268        account_pending_keys = CFDictionaryCopyKeys(full_list);
269        CFDictionaryAddValue(changes, account, account_pending_keys);
270        CFReleaseSafe(account_pending_keys); // The dictionary keeps it, we don't retain it here.
271    }
272
273    CFMutableArrayRef handled = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
274
275    secerror("Changes for %@: %@", SOSTransportKeyParameterTestGetName((SOSTransportKeyParameterTestRef) account->key_transport), account_pending_keys);
276
277    CFErrorRef error = NULL;
278    CFMutableDictionaryRef account_pending_messages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
279    CFArrayForEach(account_pending_keys, ^(const void *value) {
280        CFDictionaryAddValue(account_pending_messages, value, CFDictionaryGetValue(full_list, value));
281    });
282
283    ok(handled = SOSTransportDispatchMessages(account, account_pending_messages, &error), "SOSTransportHandleMessages failed (%@)", error);
284
285    if (isArray(handled)) {
286        CFArrayForEach(handled, ^(const void *value) {
287            CFArrayRemoveAllValue(account_pending_keys, value);
288        });
289    }
290
291    CFReleaseNull(handled);
292    CFReleaseNull(error);
293}
294
295#define kFeedChangesToMultieTestCountPer 1
296
297static inline void FeedChangesToMultiV(CFMutableDictionaryRef changes, va_list argp)
298{
299    SOSAccountRef account = NULL;
300    while((account = va_arg(argp, SOSAccountRef)) != NULL) {
301        FeedChangesTo(changes, account);
302    }
303}
304
305static inline void FeedChangesToMulti(CFMutableDictionaryRef changes, ...)
306{
307    va_list argp;
308    va_start(argp, changes);
309
310    FeedChangesToMultiV(changes, argp);
311
312    va_end(argp);
313}
314
315static inline void InjectChangeToMulti(CFMutableDictionaryRef changes,
316                                       CFStringRef changeKey, CFTypeRef changeValue, ...)
317{
318    CFMutableDictionaryRef changes_to_send = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
319                                                                                     changeKey, changeValue,
320                                                                                     NULL);
321    AddNewChanges(changes, changes_to_send, NULL);
322    CFReleaseNull(changes_to_send);
323
324    va_list argp;
325    va_start(argp, changeValue);
326    FeedChangesToMultiV(changes, argp);
327
328}
329
330
331static inline bool ProcessChangesOnceV(CFMutableDictionaryRef changes, va_list argp)
332{
333    bool result = FillAllChanges(changes);
334
335    FeedChangesToMultiV(changes, argp);
336
337    return result;
338}
339
340
341static inline bool ProcessChangesOnce(CFMutableDictionaryRef changes, ...)
342{
343    va_list argp;
344    va_start(argp, changes);
345
346    bool result = ProcessChangesOnceV(changes, argp);
347
348    va_end(argp);
349
350    return result;
351}
352
353static inline int ProcessChangesUntilNoChange(CFMutableDictionaryRef changes, ...)
354{
355    va_list argp;
356    va_start(argp, changes);
357
358    int result = 0;
359    bool new_data = false;
360    do {
361        va_list argp_copy;
362        va_copy(argp_copy, argp);
363
364        new_data = ProcessChangesOnceV(changes, argp_copy);
365
366        ++result;
367
368        va_end(argp_copy);
369    } while (new_data);
370
371    va_end(argp);
372
373    return result;
374
375}
376
377//
378// MARK: Account creation
379//
380
381static SOSAccountRef CreateAccountForLocalChanges(CFStringRef name, CFStringRef data_source_name)
382{
383    SOSDataSourceFactoryRef factory = SOSTestDataSourceFactoryCreate();
384    SOSDataSourceRef ds = SOSTestDataSourceCreate();
385    SOSTestDataSourceFactoryAddDataSource(factory, data_source_name, ds);
386    SOSEngineRef engine = SOSEngineCreate(ds, NULL);
387    ds->engine = engine;
388    CFDictionaryRef gestalt = SOSCreatePeerGestaltFromName(name);
389
390    SOSAccountRef result = SOSAccountCreateTest(kCFAllocatorDefault, name, gestalt, factory);
391
392    CFReleaseNull(gestalt);
393
394    return result;
395}
396
397
398static inline int countPeers(SOSAccountRef account) {
399    CFErrorRef error = NULL;
400    CFArrayRef peers;
401
402    peers = SOSAccountCopyPeers(account, &error);
403    int retval = (int) CFArrayGetCount(peers);
404    CFReleaseNull(error);
405    CFReleaseNull(peers);
406    return retval;
407}
408
409static inline int countActivePeers(SOSAccountRef account) {
410    CFErrorRef error = NULL;
411    CFArrayRef peers;
412
413    peers = SOSAccountCopyActivePeers(account, &error);
414    int retval = (int) CFArrayGetCount(peers);
415    CFReleaseNull(error);
416    CFReleaseNull(peers);
417    return retval;
418}
419
420static inline int countActiveValidPeers(SOSAccountRef account) {
421    CFErrorRef error = NULL;
422    CFArrayRef peers;
423
424    peers = SOSAccountCopyActiveValidPeers(account, &error);
425    int retval = (int) CFArrayGetCount(peers);
426    CFReleaseNull(error);
427    CFReleaseNull(peers);
428    return retval;
429}
430
431static inline int countApplicants(SOSAccountRef account) {
432    CFErrorRef error = NULL;
433    CFArrayRef applicants = SOSAccountCopyApplicants(account, &error);
434    int retval = 0;
435
436    if(applicants) retval = (int)CFArrayGetCount(applicants);
437    CFReleaseNull(error);
438    CFReleaseNull(applicants);
439    return retval;
440}
441
442
443static inline void showActiveValidPeers(SOSAccountRef account) {
444    CFErrorRef error = NULL;
445    CFArrayRef peers;
446
447    peers = SOSAccountCopyActiveValidPeers(account, &error);
448    CFArrayForEach(peers, ^(const void *value) {
449        SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
450        ok(0, "Active Valid Peer %@", pi);
451    });
452    CFReleaseNull(peers);
453}
454
455#endif
456