1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package edu.internet2.middleware.changelogconsumer.googleapps;
17  
18  import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
19  import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
20  import com.google.api.client.http.HttpTransport;
21  import com.google.api.client.json.JsonFactory;
22  import com.google.api.client.json.jackson2.JacksonFactory;
23  import com.google.api.services.admin.directory.Directory;
24  import com.google.api.services.admin.directory.model.Group;
25  import com.google.api.services.admin.directory.model.Member;
26  import com.google.api.services.admin.directory.model.User;
27  import com.google.api.services.admin.directory.model.UserName;
28  import edu.internet2.middleware.changelogconsumer.googleapps.cache.GoogleCacheManager;
29  import edu.internet2.middleware.changelogconsumer.googleapps.utils.AddressFormatter;
30  import edu.internet2.middleware.grouper.changeLog.ChangeLogEntry;
31  import edu.internet2.middleware.grouper.changeLog.ChangeLogLabels;
32  import edu.internet2.middleware.grouper.changeLog.ChangeLogProcessorMetadata;
33  import edu.internet2.middleware.grouper.changeLog.ChangeLogType;
34  import edu.internet2.middleware.subject.Subject;
35  import edu.internet2.middleware.subject.provider.SubjectTypeEnum;
36  import org.junit.After;
37  import org.junit.Before;
38  import org.junit.BeforeClass;
39  import org.junit.Test;
40  import org.junit.runner.RunWith;
41  import org.powermock.core.classloader.annotations.PowerMockIgnore;
42  import org.powermock.core.classloader.annotations.PrepareForTest;
43  import org.powermock.modules.junit4.PowerMockRunner;
44  
45  import java.io.IOException;
46  import java.io.InputStream;
47  import java.math.BigInteger;
48  import java.security.GeneralSecurityException;
49  import java.security.SecureRandom;
50  import java.util.ArrayList;
51  import java.util.Arrays;
52  import java.util.List;
53  import java.util.Properties;
54  
55  import static junit.framework.Assert.assertNotNull;
56  import static junit.framework.Assert.assertTrue;
57  import static org.junit.Assert.assertEquals;
58  import static org.mockito.Mockito.when;
59  import static org.powermock.api.mockito.PowerMockito.mock;
60  
61  @RunWith(PowerMockRunner.class)
62  @PowerMockIgnore({"javax.net.ssl.*", "org.apache.log4j.*", " org.apache.logging.log4j.*"})
63  @PrepareForTest(value = { })
64  public class GoogleAppsChangeLogConsumerTest {
65  
66      private static final String groupName = "qsuob:testStem:test";
67      private String groupDisplayName = "test";
68  
69      private static final String subjectId = "fiwi";
70      private static final String sourceId = "jdbc";
71  
72      private ChangeLogProcessorMetadata metadata;
73      private static AddressFormatter addressFormatter = new AddressFormatter();
74  
75      private static GoogleAppsChangeLogConsumer consumer;
76      private static String googleDomain;
77  
78      
79      private static HttpTransport httpTransport;
80  
81      
82      private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
83  
84      private static Directory directory = null;
85  
86      @BeforeClass
87      public static void setupClass() {
88  
89          consumer = new GoogleAppsChangeLogConsumer();
90  
91          Properties props = new Properties();
92  
93          InputStream is = ClassLoader.getSystemResourceAsStream("unit-test.properties");
94          try {
95              props.load(is);
96  
97              googleDomain = props.getProperty("DOMAIN");
98              addressFormatter
99                      .setDomain(googleDomain)
100                     .setGroupIdentifierExpression(props.getProperty("GROUP_IDENTIFIER_EXPRESSION"))
101                     .setSubjectIdentifierExpression(props.getProperty("SUBJECT_IDENTIFIER_EXPRESSION"));
102 
103             httpTransport = GoogleNetHttpTransport.newTrustedTransport();
104 
105             GoogleCredential googleCredential = null;
106                  googleCredential = GoogleAppsSdkUtils.getGoogleDirectoryCredential(props.getProperty("SERVICE_ACCOUNT_EMAIL"),
107                          props.getProperty("SERVICE_ACCOUNT_PKCS_12_FILE_PATH"), props.getProperty("SERVICE_IMPERSONATION_USER"),
108                          httpTransport, JSON_FACTORY);
109 
110             directory = new Directory.Builder(httpTransport, JSON_FACTORY, googleCredential)
111                     .setApplicationName("Google Apps Grouper Provisioner")
112                     .build();
113             
114         } catch (Exception e) {
115             System.out.println("unit-test.properties configuration not found. Try again! Love, Grumpy Cat");
116         }
117 
118     }
119 
120     @Before
121     public void setup() throws GeneralSecurityException, IOException {
122         metadata = mock(ChangeLogProcessorMetadata.class);
123         when(metadata.getConsumerName()).thenReturn("google");
124     }
125 
126     @After
127     public void tearDown() throws GeneralSecurityException, IOException {
128         try {
129             GoogleAppsSdkUtils.removeGroup(directory, addressFormatter.qualifyGroupAddress(groupName));
130         } catch (IOException e) {
131 
132         }
133 
134         try {
135             GoogleAppsSdkUtils.removeUser(directory, addressFormatter.qualifyGroupAddress(subjectId));
136         } catch (IOException e) {
137 
138         }
139 
140         GoogleCacheManager.googleGroups().clear();
141         GoogleCacheManager.googleUsers().clear();
142 
143         
144         pause(1000L);
145     }
146 
147     @Test
148     public void testProcessGroupAdd() throws GeneralSecurityException, IOException {
149         ChangeLogEntry addEntry = mock(ChangeLogEntry.class);
150         when(addEntry.getChangeLogType()).thenReturn(new ChangeLogType("group", "addGroup", ""));
151         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_ADD.name)).thenReturn(groupName);
152         when(addEntry.getContextId()).thenReturn("123456789");
153 
154         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(addEntry));
155 
156         consumer.processChangeLogEntries(changeLogEntryList, metadata);
157         Group group = GoogleAppsSdkUtils.retrieveGroup(directory, addressFormatter.qualifyGroupAddress(groupName));
158         assertNotNull(group);
159         assertTrue(group.getName().equalsIgnoreCase(groupDisplayName));
160     }
161 
162     @Test
163     public void testProcessGroupUpdate() throws GeneralSecurityException, IOException {
164         final String NEW_TEST = "newTest";
165 
166         createTestGroup(groupDisplayName, groupName);
167 
168         ChangeLogEntry addEntry = mock(ChangeLogEntry.class);
169         when(addEntry.getChangeLogType()).thenReturn(new ChangeLogType("group", "updateGroup", ""));
170         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.name)).thenReturn(groupName);
171         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.propertyChanged)).thenReturn("displayExtension");
172         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.propertyOldValue)).thenReturn(groupDisplayName);
173         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.propertyNewValue)).thenReturn(NEW_TEST);
174         when(addEntry.getContextId()).thenReturn("123456789");
175 
176         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(addEntry));
177 
178         consumer.processChangeLogEntries(changeLogEntryList, metadata);
179         pause(1000L);
180         Group group = GoogleAppsSdkUtils.retrieveGroup(directory, addressFormatter.qualifyGroupAddress(groupName));
181 
182         assertNotNull(group);
183         assertEquals(NEW_TEST, group.getName());
184 
185         
186         
187         
188     }
189 
190 
191     @Test
192     public void testProcessGroupMemberAddExistingUser() throws GeneralSecurityException, IOException {
193         
194         createTestGroup(groupDisplayName, groupName);
195         createTestUser(buildSubjectAddress(subjectId), "Fiona", "Windsor");
196 
197         ChangeLogEntry addEntry = mock(ChangeLogEntry.class);
198         when(addEntry.getChangeLogType()).thenReturn(new ChangeLogType("membership", "addMembership", ""));
199         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.groupName)).thenReturn(groupName);
200         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.subjectId)).thenReturn(subjectId);
201         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.sourceId)).thenReturn(sourceId);
202         when(addEntry.getContextId()).thenReturn("123456789");
203 
204         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(addEntry));
205 
206         consumer.processChangeLogEntries(changeLogEntryList, metadata);
207 
208         List<Member> members = GoogleAppsSdkUtils.retrieveGroupMembers(directory, addressFormatter.qualifyGroupAddress(groupName));
209         assertNotNull(members);
210         assertTrue(members.size() == 1);
211         assertTrue(members.get(0).getEmail().equalsIgnoreCase(buildSubjectAddress(subjectId)));
212     }
213 
214 
215 
216 
217 
218 
219 
220 
221 
222 
223 
224 
225 
226 
227 
228 
229 
230 
231 
232 
233 
234 
235 
236 
237     
238     @Test
239     public void testProcessGroupMemberAddNewUserWithProvisioning() throws GeneralSecurityException, IOException {
240         
241         createTestGroup(groupDisplayName, groupName);
242 
243         ChangeLogEntry addEntry = mock(ChangeLogEntry.class);
244         when(addEntry.getChangeLogType()).thenReturn(new ChangeLogType("membership", "addMembership", ""));
245         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.groupName)).thenReturn(groupName);
246         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.subjectId)).thenReturn(subjectId);
247         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_ADD.sourceId)).thenReturn(sourceId);
248         when(addEntry.getContextId()).thenReturn("123456789");
249 
250         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(addEntry));
251 
252         consumer.processChangeLogEntries(changeLogEntryList, metadata);
253 
254         List<Member> members = GoogleAppsSdkUtils.retrieveGroupMembers(directory, addressFormatter.qualifyGroupAddress(groupName));
255         assertNotNull(members);
256         assertTrue(members.size() == 1);
257         assertTrue(members.get(0).getEmail().equalsIgnoreCase(buildSubjectAddress(subjectId)));
258     }
259 
260     @Test
261     public void testProcessGroupMemberRemove() throws GeneralSecurityException, IOException {
262         
263         Group group = createTestGroup(groupDisplayName, groupName);
264         createTestUser(buildSubjectAddress(subjectId), "Fiona", "Windsor");
265 
266         Member member = new Member()
267                 .setEmail(buildSubjectAddress(subjectId))
268                 .setRole("MEMBER");
269         GoogleAppsSdkUtils.addGroupMember(directory, group.getEmail(), member);
270 
271         ChangeLogEntry addEntry = mock(ChangeLogEntry.class);
272         when(addEntry.getChangeLogType()).thenReturn(new ChangeLogType("membership", "deleteMembership", ""));
273         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_DELETE.groupName)).thenReturn(groupName);
274         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_DELETE.subjectId)).thenReturn(subjectId);
275         when(addEntry.retrieveValueForLabel(ChangeLogLabels.MEMBERSHIP_DELETE.sourceId)).thenReturn(sourceId);
276         when(addEntry.getContextId()).thenReturn("123456789");
277 
278         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(addEntry));
279 
280         consumer.processChangeLogEntries(changeLogEntryList, metadata);
281 
282         List<Member> members = GoogleAppsSdkUtils.retrieveGroupMembers(directory, addressFormatter.qualifyGroupAddress(groupName));
283         assertNotNull(members);
284         assertTrue(members.size() == 0);
285     }
286 
287     @Test
288     public void testProcessGroupsStemChange() throws GeneralSecurityException, IOException {
289         try {
290             createTestGroup(groupDisplayName, groupName + "Change");
291         } catch (Exception ex) {}
292 
293         ChangeLogEntry addEntry = mock(ChangeLogEntry.class);
294         when(addEntry.getChangeLogType()).thenReturn(new ChangeLogType("group", "updateGroup", ""));
295         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.name)).thenReturn(groupName);
296         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.propertyChanged)).thenReturn("name");
297         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.propertyOldValue)).thenReturn(groupName+"Change");
298         when(addEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_UPDATE.propertyNewValue)).thenReturn(groupName);
299         when(addEntry.getContextId()).thenReturn("123456789");
300 
301         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(addEntry));
302 
303         consumer.processChangeLogEntries(changeLogEntryList, metadata);
304         pause(2000L);
305         Group group = GoogleAppsSdkUtils.retrieveGroup(directory, addressFormatter.qualifyGroupAddress(groupName));
306 
307         assertNotNull(group);
308         assertEquals(addressFormatter.qualifyGroupAddress(groupName).toLowerCase(), group.getEmail());
309         assertTrue(group.getAliases().contains(addressFormatter.qualifyGroupAddress(groupName+"Change")));
310     }
311 
312     @Test
313     public void testProcessGroupDelete() throws GeneralSecurityException, IOException {
314         createTestGroup(groupDisplayName, groupName);
315 
316         ChangeLogEntry deleteEntry = mock(ChangeLogEntry.class);
317         when(deleteEntry.getChangeLogType()).thenReturn(new ChangeLogType("group", "deleteGroup", ""));
318         when(deleteEntry.retrieveValueForLabel(ChangeLogLabels.GROUP_DELETE.name)).thenReturn(groupName);
319         when(deleteEntry.getContextId()).thenReturn("123456789");
320 
321         ArrayList<ChangeLogEntry> changeLogEntryList = new ArrayList<ChangeLogEntry>(Arrays.asList(deleteEntry));
322 
323         consumer.processChangeLogEntries(changeLogEntryList, metadata);
324         assertTrue(GoogleAppsSdkUtils.retrieveGroup(directory, addressFormatter.qualifyGroupAddress(groupName)) == null);
325     }
326 
327 
328 
329 
330 
331 
332 
333 
334 
335 
336 
337 
338 
339 
340 
341 
342 
343 
344 
345 
346 
347 
348 
349 
350 
351 
352 
353 
354 
355 
356 
357 
358 
359 
360 
361 
362 
363 
364 
365 
366     private Group createTestGroup(String name, String mailbox) throws IOException {
367         Group group = new Group();
368         group.setName(name);
369         group.setEmail(addressFormatter.qualifyGroupAddress(mailbox));
370         return GoogleAppsSdkUtils.addGroup(directory, group);
371     }
372 
373     private User createTestUser(String email, String givenName, String surname) throws IOException {
374         User user = new User();
375         user.setPrimaryEmail(email);
376         user.setName(new UserName());
377         user.getName().setFamilyName(surname);
378         user.getName().setGivenName(givenName);
379         user.setPassword(new BigInteger(130, new SecureRandom()).toString(32));
380         return GoogleAppsSdkUtils.addUser(directory, user);
381     }
382 
383     private Subject getTestUserSubject() {
384         Subject subject = mock(Subject.class);
385         when(subject.getAttributeValue("givenName")).thenReturn("testgn2");
386         when(subject.getAttributeValue("sn")).thenReturn("testfn2");
387         when(subject.getAttributeValue("displayName")).thenReturn("testgn2, testfn2");
388         when(subject.getAttributeValue("mail")).thenReturn(buildSubjectAddress(subjectId));
389         when(subject.getType()).thenReturn(SubjectTypeEnum.PERSON);
390         return subject;
391     }
392 
393     private Subject getTestGroupSubject() {
394         Subject subject = mock(Subject.class);
395         when(subject.getAttributeValue("givenName")).thenReturn("testgn2");
396         when(subject.getAttributeValue("sn")).thenReturn("testfn2");
397         when(subject.getAttributeValue("displayName")).thenReturn("testgn2, testfn2");
398         when(subject.getAttributeValue("mail")).thenReturn(buildSubjectAddress(subjectId));
399         when(subject.getType()).thenReturn(SubjectTypeEnum.GROUP);
400         return subject;
401     }
402 
403     private void pause(long milliseconds) {
404         try {
405             Thread.sleep(milliseconds);
406         } catch (InterruptedException e) {
407             e.printStackTrace();
408         }
409     }
410 
411     private String buildSubjectAddress(String subjectId) {
412         return String.format("%s@%s", subjectId, googleDomain);
413     }
414 
415 }