View Javadoc
1   /**
2    * Copyright 2014 Internet2
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package edu.internet2.middleware.grouper.scim;
17  
18  import edu.internet2.middleware.grouper.Group;
19  import edu.internet2.middleware.grouper.Member;
20  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
21  import org.apache.log4j.Logger;
22  import org.apache.wink.client.ClientConfig;
23  import org.apache.wink.client.ClientWebException;
24  import org.apache.wink.client.handlers.ClientHandler;
25  import org.apache.wink.client.Resource;
26  import org.apache.wink.client.RestClient;
27  import org.wso2.charon.core.client.SCIMClient;
28  import org.wso2.charon.core.exceptions.BadRequestException;
29  import org.wso2.charon.core.exceptions.CharonException;
30  import org.wso2.charon.core.schema.SCIMConstants;
31  import org.wso2.charon.samples.utils.CharonResponseHandler;
32  import org.wso2.charon.utils.authentication.BasicAuthHandler;
33  import org.wso2.charon.utils.authentication.BasicAuthInfo;
34  
35  //TODO -- should we be using groupID or groupName()?
36  /**
37   * Emits SCIM.  
38   * @author David Langenberg <davel@uchicago.edu>
39   */
40  public class ScimEmitter {
41  
42    protected static Logger log = Logger.getLogger(ScimEmitter.class);
43  
44    protected SCIMClient scimClient;
45    protected CharonResponseHandler crh;
46    protected RestClient restClient;
47    
48    protected GrouperConfig gConf;
49  
50    private BasicAuthInfo encodedBasicAuthInfo;
51  
52  
53    public ScimEmitter() {
54  	gConf = GrouperConfig.retrieveConfig();
55  	  
56      ClientConfig config = new ClientConfig();
57      crh = new CharonResponseHandler();
58      config.handlers(new ClientHandler[]{crh});
59      restClient = new RestClient(config);
60  
61      //create a apache wink ClientHandler to intercept and identify response messages  
62      CharonResponseHandler responseHandler = new CharonResponseHandler();
63      responseHandler.setSCIMClient(scimClient);
64      //set the handler in wink client config  
65      ClientConfig clientConfig = new ClientConfig();
66      clientConfig.handlers(new ClientHandler[]{responseHandler});
67      //create a wink rest client with the above config  
68      restClient = new RestClient(clientConfig);
69      //create resource endpoint to access group resource  
70      Resource groupResource = restClient.resource(gConf.propertyValueString("scim.endpoint"));
71  	
72      BasicAuthInfo basicAuthInfo = new BasicAuthInfo();
73      //set creds
74      basicAuthInfo.setUserName(gConf.propertyValueString("scim.user"));
75      basicAuthInfo.setPassword(gConf.propertyValueString("scim.password"));
76  
77      BasicAuthHandler basicAuthHandler = new BasicAuthHandler();
78      encodedBasicAuthInfo = (BasicAuthInfo) basicAuthHandler.getAuthenticationToken(basicAuthInfo);
79  
80      scimClient = new SCIMClient();
81    }
82  
83    /**
84     * creates a SCIM Group out of a grouper group
85     * @param group
86     * @return 
87     */
88    public String createGroup(Group group) {
89      try {
90        
91        org.wso2.charon.core.objects.Group scimGroup = scimClient.createGroup();
92        
93        scimGroup.setDisplayName(group.getDisplayName());
94        scimGroup.setExternalId(group.getName());
95        
96        for(Member m : group.getMembers()){
97          scimGroup.setMember(m.getSubjectId());
98        }
99        
100       //create resource endpoint to access group resource  
101       //no groupid appended to this one because that's somethign the server will handle
102       Resource groupResource = restClient.resource(gConf.propertyValueString("scim.endpoint"));
103       
104       return groupResource.
105               header(SCIMConstants.AUTHORIZATION_HEADER, encodedBasicAuthInfo.getAuthorizationHeader()).
106               contentType(SCIMConstants.APPLICATION_JSON).accept(SCIMConstants.APPLICATION_JSON).
107               post(String.class, scimClient.encodeSCIMObject(scimGroup, SCIMConstants.JSON));
108     } catch (CharonException e) {
109       throw new RuntimeException(e);
110     } catch (ClientWebException e){
111 		System.err.println(e.getMessage());
112 		System.err.println(e.getResponse().getMessage());
113 		log.error(e.getMessage(),e);
114 		throw new RuntimeException(e);
115 	}
116   }
117   
118   /**
119    * This updates a group which already exists in the SCIM endpoint.  According to the
120    * SURFNet use-case, we can only send full-groups, not updates, so update will
121    * for now be a delete & create operation
122    * @param group
123    * @return 
124    */
125   public String updateGroup(Group group){
126     deleteGroup(group);
127 	return createGroup(group);
128   }
129   
130   /**
131    * Retrieves the SCIM server's group representation of the Grouper group.
132    * @param group grouper group to query the SCIM server for
133    * @return 
134    */
135   public org.wso2.charon.core.objects.Group getGroup(Group group) {
136     //todo -- should this be protected or private?
137     //create resource endpoint to access a known user resource.
138     Resource groupResource = restClient.resource(gConf.propertyValueString("scim.endpoint") + group.getName());
139     String response = groupResource.
140             header(SCIMConstants.AUTHORIZATION_HEADER, encodedBasicAuthInfo.getAuthorizationHeader()).
141             contentType(SCIMConstants.APPLICATION_JSON).accept(SCIMConstants.APPLICATION_JSON)
142             .get(String.class);
143     try {
144       org.wso2.charon.core.objects.Group scimGroup = (org.wso2.charon.core.objects.Group) scimClient.decodeSCIMResponse(response, SCIMConstants.JSON, 2);
145       return scimGroup;
146     } catch (BadRequestException e) {
147       //TODO decide what to do
148       throw new RuntimeException(e);
149     } catch (CharonException e) {
150       //TODO decide what to do
151       throw new RuntimeException(e);
152     }
153 
154   }
155     
156   public String deleteGroup(Group group) {
157 
158     //create resource endpoint to access group resource  
159     //no groupid appended to this one because that's somethign the server will handle
160     Resource groupResource = restClient.resource(gConf.propertyValueString("scim.endpoint") + group.getName());
161 
162     return groupResource.
163             header(SCIMConstants.AUTHORIZATION_HEADER, encodedBasicAuthInfo.getAuthorizationHeader()).
164             accept(SCIMConstants.APPLICATION_JSON).
165             delete(String.class);
166 
167   }
168 
169 }