root / spepintegrators / confluencejira / src / com / qut / middleware / spep / integrators / atlassian / ConfluenceJiraIntegrator.java @ 460f2a096fd9063f77651e57bf9854311aed62c4

View | Annotate | Download (15.9 KB)

1
/* 
2
 * Copyright 2007, Queensland University of Technology
3
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
4
 * use this file except in compliance with the License. You may obtain a copy of 
5
 * the License at 
6
 * 
7
 *   http://www.apache.org/licenses/LICENSE-2.0 
8
 * 
9
 * Unless required by applicable law or agreed to in writing, software 
10
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
11
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
12
 * License for the specific language governing permissions and limitations under 
13
 * the License.
14
 * 
15
 * Author: Bradley Beddoes
16
 * Creation Date: 06/06/2007
17
 * 
18
 * Purpose: Integrates ESOE / SPEP with Confluence and Jira
19
 */
20
package com.qut.middleware.spep.integrators.atlassian;
21
22
import java.io.IOException;
23
import java.io.InputStream;
24
import java.security.Principal;
25
import java.util.HashMap;
26
import java.util.List;
27
import java.util.Properties;
28
29
import javax.servlet.http.HttpServletRequest;
30
import javax.servlet.http.HttpServletResponse;
31
import javax.servlet.http.HttpSession;
32
33
import org.apache.log4j.Logger;
34
35
//import bucket.container.ContainerManager;
36
37
import com.atlassian.spring.container.ContainerManager;
38
import com.atlassian.seraph.auth.AuthenticatorException;
39
//import com.atlassian.seraph.auth.DefaultAuthenticator;
40
import com.atlassian.confluence.user.ConfluenceAuthenticator;
41
import com.atlassian.confluence.user.UserAccessor;
42
import com.atlassian.confluence.user.PersonalInformationManager;
43
import com.atlassian.confluence.user.PersonalInformation;
44
import com.atlassian.crowd.embedded.api.CrowdService;
45
import com.atlassian.crowd.embedded.impl.ImmutableUser;
46
import com.atlassian.user.EntityException;
47
import com.atlassian.user.Group;
48
import com.atlassian.user.GroupManager;
49
import com.atlassian.user.User;
50
//import com.atlassian.user.impl.DefaultUser;
51
//import com.atlassian.user.UserManager;
52
import com.atlassian.confluence.*;
53
import com.qut.middleware.spep.filter.SPEPFilter;
54
55
public class ConfluenceJiraIntegrator extends ConfluenceAuthenticator
56
{
57
        private static final long serialVersionUID = 234789254349L;
58
59
        private final String DEFAULT_GROUP_NAME = "confluence-users";
60
        private final String ANONYMOUS_DEFAULT_ACCOUNT = "esoe-confluence-anon";
61
62
        //private UserManager userManager;
63
        private GroupManager groupManager;
64
        private Group defaultGroup;
65
66
        private Properties props;
67
68
        private String userIDAttribute;
69
        private String fullNameAttribute;
70
        private String mailAttribute;
71
        private String rolesAttribute;
72
        private String logoutURL;
73
74
        /* Local logging instance */
75
        private Logger logger = Logger.getLogger(ConfluenceJiraIntegrator.class.getName());
76
77
        public ConfluenceJiraIntegrator()
78
        {
79
                InputStream propStream = ConfluenceJiraIntegrator.class.getResourceAsStream("integrator.properties");
80
                
81
                if(propStream == null)
82
                {
83
                        this.logger.fatal("Could not load intial properties file, could not be found in path, terminating startup");
84
                        throw new IllegalArgumentException("Could not load intial properties file, could not be found in path, terminating startup");
85
                }
86
                
87
                props = new Properties();
88
                try
89
                {
90
                        props.load(propStream);
91
                        
92
                        this.userIDAttribute = props.getProperty("userID");
93
                        if(this.userIDAttribute == null)
94
                        {
95
                                this.logger.fatal("Could not load value for userID property");
96
                                throw new IllegalArgumentException("Could not load value for userID property");
97
                        }
98
                        
99
                        this.fullNameAttribute = props.getProperty("fullName");
100
                        if(this.fullNameAttribute == null)
101
                        {
102
                                this.logger.fatal("Could not load value for fullName property");
103
                                throw new IllegalArgumentException("Could not load value for fullName property");
104
                        }
105
                        
106
                        this.mailAttribute = props.getProperty("mail");
107
                        if(this.mailAttribute == null)
108
                        {
109
                                this.logger.fatal("Could not load value for mail property");
110
                                throw new IllegalArgumentException("Could not load value for mail property");
111
                        }
112
                        
113
                        this.rolesAttribute = props.getProperty("roles");
114
                        if(this.rolesAttribute == null)
115
                        {
116
                                this.logger.fatal("Could not load value for roles property");
117
                                throw new IllegalArgumentException("Could not load value for roles property");
118
                        }
119
                        
120
                        this.logoutURL = props.getProperty("logoutURL");
121
                        if(this.logoutURL == null)
122
                        {
123
                                this.logger.fatal("Could not load value for logoutURL  property");
124
                                throw new IllegalArgumentException("Could not load value for logoutURL property");
125
                        }
126
                        
127
                }
128
                catch (IOException e)
129
                {
130
                        this.logger.fatal("Could not load intial properties file, terminating startup");
131
                        throw new IllegalArgumentException("Could not load intial properties file, terminating startup", e);
132
                }
133
        }
134
135
        /*
136
         * (non-Javadoc)
137
         * 
138
         * @see com.atlassian.seraph.auth.DefaultAuthenticator#logout(javax.servlet.http.HttpServletRequest,
139
         *      javax.servlet.http.HttpServletResponse)
140
         */
141
        @Override
142
        public boolean logout(HttpServletRequest request, HttpServletResponse response) throws AuthenticatorException
143
        {
144
                super.logout(request, response);
145
                
146
                HttpSession httpSession = request.getSession();
147
                try
148
                {
149
                        this.logger.debug("Logging out user from current confluence session");
150
                        request.getSession().setAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY, null);
151
                        request.getSession().setAttribute(LOGGED_OUT_KEY, Boolean.TRUE);
152
                        response.sendRedirect(this.logoutURL);
153
                }
154
                catch (IOException e)
155
                {
156
                        this.logger.error(e.getLocalizedMessage());
157
                        this.logger.debug(e);
158
                        return false;
159
                }
160
161
                return true;
162
        }
163
164
        /*
165
         * (non-Javadoc)
166
         * 
167
         * @see com.atlassian.seraph.auth.DefaultAuthenticator#getUser(javax.servlet.http.HttpServletRequest,
168
         *      javax.servlet.http.HttpServletResponse)
169
         */
170
        @Override
171
        public Principal getUser(HttpServletRequest request, HttpServletResponse response)
172
        {
173
                UserAccessor userAccessor = getUserAccessor();
174
175
                this.groupManager = (GroupManager) ContainerManager.getComponent("groupManager");
176
177
                try
178
                {
179
                        this.defaultGroup = this.groupManager.getGroup(this.DEFAULT_GROUP_NAME);
180
                }
181
                catch (Exception e)
182
                {
183
                        this.logger.error("Attempted to get group " + this.DEFAULT_GROUP_NAME + " but the group does not exist.");
184
                        throw new RuntimeException("Attempted to get group " + this.DEFAULT_GROUP_NAME + " but the group does not exist.");
185
                }
186
187
188
                if (groupManager == null)
189
                {
190
                        throw new RuntimeException("groupManager was not wired in ConfluenceJiraAuthenticator");
191
            }
192
193
194
                HttpSession httpSession = request.getSession();
195
                User user;
196
                List<Object> userIdentifiers;
197
198
                HashMap<String, List<Object>> attributeMap = (HashMap<String, List<Object>>) httpSession.getAttribute(SPEPFilter.ATTRIBUTES);
199
                
200
                /* Possible lazy init or no attributes sent from ESOE */
201
                if (attributeMap == null)
202
                        return null;
203
204
                userIdentifiers = attributeMap.get(this.userIDAttribute);
205
206
                /* Check if the principal is already logged in */
207
                if (httpSession != null && httpSession.getAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY) != null)
208
                {
209
                        user = (User) httpSession.getAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY);
210
                        if (user != null)
211
                                return user;
212
                }
213
214
                /* Either the user has not logged in or a different user is now active */
215
                httpSession.removeAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY);
216
217
                /*
218
                 * If we haven't been supplied with a user identifier attribute, map them to a default anonymous account that has
219
                 * little to no privilledges
220
                 */
221
                if (userIdentifiers == null)
222
                {
223
                        this.logger.debug("loading anonymous user, couldn't find any user identifiers");
224
                        return this.loadAnonymousUser(httpSession, response);
225
                }
226
227
                this.logger.debug("Doing user login to confluence/jira");
228
                for (Object id : userIdentifiers)
229
                {
230
                        try
231
                        {
232
                                user = userAccessor.getUser((String) id);
233
                                if (user != null)
234
                                {
235
                                        this.logger.debug("Found existing user identified by " + (String) id + " establishing session for that user");
236
                                        updateAttributes(user, attributeMap);
237
                                        updateGroupMembership(user, attributeMap, response);
238
                                        httpSession.setAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY, user);
239
                                        return user;
240
                                }
241
                        }
242
                        catch (Throwable e)
243
                        {
244
                                this.logger.error("Unable to create new user for confluence/jira - " + e.getLocalizedMessage());
245
                                this.logger.debug(e);
246
                                errorResponse(response);
247
                        }
248
                }
249
250
                /* User is not currently registered in confluence/jira dynamically provision an account */
251
                user = registerUserAccount(userIdentifiers);
252
253
                if (user == null)
254
                {
255
                        errorResponse(response);
256
                        return null;
257
                }
258
259
                updateAttributes(user, attributeMap);
260
                updateGroupMembership(user, attributeMap, response);
261
                httpSession.setAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY, user);
262
                httpSession.setAttribute(ConfluenceAuthenticator.LOGGED_OUT_KEY, null);
263
264
                /* When we have a new user account show them the dashboard */
265
                /*try
266
                {
267
                        response.sendRedirect("/dashboard.action");
268
                }
269
                catch (IOException e)
270
                {
271
                        // TODO Auto-generated catch block
272
                        e.printStackTrace();
273
                }*/
274
                
275
                return user;
276
        }
277
278
        private User loadAnonymousUser(HttpSession httpSession, HttpServletResponse response)
279
        {
280
                try
281
                {
282
                        UserAccessor userAccessor = getUserAccessor();
283
                        User user = userAccessor.getUser(this.ANONYMOUS_DEFAULT_ACCOUNT);
284
                        if (user != null)
285
                        {
286
                                httpSession.setAttribute(ConfluenceAuthenticator.LOGGED_IN_KEY, user);
287
                                return user;
288
                        }
289
                        else
290
                        {
291
                                this.logger.error("Unable to create anonymous user session - default anonymous account " + this.ANONYMOUS_DEFAULT_ACCOUNT + "is not present in confluence");
292
                                errorResponse(response);
293
                                return null;
294
                        }
295
                }
296
                catch (Exception e)
297
                {
298
                        this.logger.error("Unable to create anonymous user session - default anonymous account " + this.ANONYMOUS_DEFAULT_ACCOUNT + "is not present in confluence");
299
                        this.logger.debug(e);
300
                        errorResponse(response);
301
                        return null;
302
                }
303
        }
304
        
305
        private User registerUserAccount(List<Object> userIdentifiers)
306
        {
307
                UserAccessor userAccessor = getUserAccessor();
308
                User user;
309
310
                try
311
                {
312
                        /* Ensure we got a username, if not this user is essentially anonymous */
313
                        this.logger.info("Dynamically provisioning new account in confluence/jira for first time user identified as " + (String) userIdentifiers.get(0));
314
                        /* Create a new user, in the case the user presents with multiple identifiers... utilise the first */
315
                        user = userAccessor.createUser((String) userIdentifiers.get(0));
316
317
                        return user;
318
                }
319
                catch (Exception e)
320
                {
321
                        this.logger.error("Unable to create new user for confluence/jira - " + e.getLocalizedMessage());
322
                        this.logger.debug(e);
323
                        return null;
324
                }
325
        }
326
327
        private void updateAttributes(User user, HashMap<String, List<Object>> attributes)
328
        {
329
                UserAccessor userAccessor = getUserAccessor();
330
                if (userAccessor.isReadOnly(user))
331
                {
332
                        logger.info("not updating user, because user is read-only");
333
                        return;
334
                }
335
336
                List<Object> fullName;
337
                List<Object> emailAddresses;
338
339
                fullName = attributes.get(this.fullNameAttribute);
340
341
                /* If multiple email addresses are present we'll use the first */
342
                emailAddresses = attributes.get(this.mailAttribute);
343
344
                String fullNameStr = (String)fullName.get(0);
345
                String emailAddressStr = (String)emailAddresses.get(0);
346
347
                this.logger.debug("Updating user data in confluence/jira for user " + user.getName());
348
349
                boolean updated = false;
350
351
                CrowdService crowdService = getCrowdService();
352
                if (crowdService == null)
353
                {
354
                        throw new RuntimeException("crowdService was not wired in ConfluenceJiraAuthenticator");
355
                }
356
357
                com.atlassian.crowd.embedded.api.User crowdUser = crowdService.getUser(user.getName());
358
359
                ImmutableUser.Builder userBuilder = new ImmutableUser.Builder();
360
                // clone the user before making mods
361
                userBuilder.active(crowdUser.isActive());
362
                userBuilder.directoryId(crowdUser.getDirectoryId());
363
                userBuilder.displayName(crowdUser.getDisplayName());
364
                userBuilder.emailAddress(crowdUser.getEmailAddress());
365
                userBuilder.name(crowdUser.getName());
366
367
                if ((fullName != null) && !fullNameStr.equals(user.getFullName()))
368
                {
369
                        logger.debug("Updating user fullName to '" + fullNameStr + "'");
370
                        userBuilder.displayName(fullNameStr);
371
                        updated = true;
372
                }
373
                else
374
                {
375
                        logger.debug("User fullName is same as old one: '" + fullNameStr + "'");
376
                }
377
378
                if ((emailAddressStr != null) && !emailAddressStr.equals(user.getEmail()))
379
                {
380
                        logger.debug("updating user emailAddress to '" + emailAddressStr + "'");
381
                        userBuilder.emailAddress(emailAddressStr);
382
                        updated = true;
383
                }
384
                else
385
                {
386
                        logger.debug("User emailAddress is same as old one: '" + emailAddressStr + "'");
387
                }
388
389
                if (updated)
390
                {
391
                        try
392
                        {
393
                                crowdService.updateUser(userBuilder.toUser());
394
                        }
395
                        catch (Throwable t)
396
                        {
397
                                logger.error("Couldn't update user " + user.getName(), t);
398
                        }
399
                }
400
401
402
                /*PersonalInformationManager personalInformationManager = getPersonalInformationManager();
403
404
                if (personalInformationManager == null)
405
                {
406
                        throw new RuntimeException("personalInformationManager was not wired in ConfluenceJiraAuthenticator");
407
            }
408
409
        PersonalInformation personalInformation = personalInformationManager.getPersonalInformation(user);
410
                if (personalInformation == null)
411
                {
412
                        this.logger.debug("personalInformation object is null. Not setting email and fullname attributes.");
413
                }
414
                else
415
                {
416
                        this.logger.debug("Got personal information object. Setting email and fullname attributes.");
417
                        this.logger.debug(personalInformation.toString());
418
                        this.logger.debug(personalInformation.getFullName());
419
                        this.logger.debug(personalInformation.getEmail());
420
421
                        if (fullName != null)
422
                        {
423
                                this.logger.debug("Setting fullname to " + (String) fullName.get(0));
424
                                personalInformation.setFullName((String) fullName.get(0));
425
                        }
426
427
                        if (emailAddresses != null)
428
                        {
429
                                this.logger.debug("Setting email address to " + (String) emailAddresses.get(0));
430
                                personalInformation.setEmail((String) emailAddresses.get(0));
431
                        }
432
433
434
                        this.logger.debug("Saving personal information object.");
435
                        this.logger.debug(personalInformation.toString());
436
                        this.logger.debug(personalInformation.getFullName());
437
                        this.logger.debug(personalInformation.getEmail());
438
                        personalInformationManager.savePersonalInformation(personalInformation, null);
439
                }*/
440
        }
441
442
        private void updateGroupMembership(User user, HashMap<String, List<Object>> attributes, HttpServletResponse response)
443
        {
444
                Group group;
445
                List<Object> roles;
446
447
                try
448
                {
449
                        /* Add user to the default confluence group otherwise they won't get anywhere */
450
                        this.groupManager.addMembership(this.defaultGroup, user);
451
452
                        roles = attributes.get(this.rolesAttribute);
453
                        if (roles != null)
454
                        {
455
                                for (Object role : roles)
456
                                {
457
458
                                        this.logger.debug("Attempting to map role " + (String) role + " to confluence/jira group");
459
                                        group = groupManager.getGroup((String) role);
460
461
                                        /*
462
                                         * Ensure group exists, admins must still manually create groups, if its non existant ignore this
463
                                         * role
464
                                         */
465
                                        if (group != null)
466
                                        {
467
                                                this.logger.debug("Mapped role " + (String) role + " to confluence/jira group of the same name, adding user permissions");
468
469
                                                /* Only add membership if they haven't been previously given it of course */
470
                                                if (!groupManager.hasMembership(group, user))
471
                                                        groupManager.addMembership(group, user);
472
                                        }
473
474
                                }
475
                        }
476
                }
477
                catch (EntityException e)
478
                {
479
                        this.logger.error("Unable to create new user for confluence/jira - " + e.getLocalizedMessage());
480
                        this.logger.debug(e);
481
                        errorResponse(response);
482
                }
483
        }
484
485
        private void errorResponse(HttpServletResponse response)
486
        {
487
                try
488
                {
489
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "While attempting to provision your account internal errors in Confluence or Jira occured, contact system administrators for resolution");
490
                }
491
                catch (IOException e)
492
                {
493
                        this.logger.error("Unable to create error response");
494
                        this.logger.debug(e);
495
                }
496
        }
497
498
        public CrowdService getCrowdService()
499
        {
500
            return (CrowdService)ContainerManager.getComponent("crowdService");
501
        }
502
503
        public GroupManager getGroupManager()
504
        {
505
            return (GroupManager)ContainerManager.getComponent("groupManager");
506
        }
507
508
        public PersonalInformationManager getPersonalInformationManager()
509
        {
510
                return (PersonalInformationManager)ContainerManager.getComponent("personalInformationManager");
511
        }
512
}