001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 * Contributors: 020 * Arnaud Kervern 021 */ 022package org.nuxeo.ecm.core.opencmis.bindings; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import javax.xml.bind.JAXBContext; 032import javax.xml.bind.JAXBElement; 033import javax.xml.bind.JAXBException; 034import javax.xml.bind.annotation.XmlAccessType; 035import javax.xml.bind.annotation.XmlAccessorType; 036import javax.xml.bind.annotation.XmlAnyAttribute; 037import javax.xml.bind.annotation.XmlAnyElement; 038import javax.xml.bind.annotation.XmlAttribute; 039import javax.xml.bind.annotation.XmlElement; 040import javax.xml.bind.annotation.XmlElementDecl; 041import javax.xml.bind.annotation.XmlID; 042import javax.xml.bind.annotation.XmlRegistry; 043import javax.xml.bind.annotation.XmlSchemaType; 044import javax.xml.bind.annotation.XmlSeeAlso; 045import javax.xml.bind.annotation.XmlType; 046import javax.xml.bind.annotation.XmlValue; 047import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; 048import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 049import javax.xml.namespace.QName; 050import javax.xml.ws.handler.MessageContext; 051import javax.xml.ws.handler.soap.SOAPHandler; 052import javax.xml.ws.handler.soap.SOAPMessageContext; 053 054import org.apache.chemistry.opencmis.commons.server.CallContext; 055import org.apache.chemistry.opencmis.server.impl.webservices.AbstractService; 056 057/** 058 * Extracts username and password from a UsernameToken 059 * 060 * @since 5.7.3 061 */ 062public class CXFAuthHandler implements SOAPHandler<SOAPMessageContext> { 063 064 protected static final JAXBContext WSSE_CONTEXT; 065 066 static { 067 try { 068 WSSE_CONTEXT = JAXBContext.newInstance(ObjectFactory.class); 069 } catch (JAXBException e) { 070 throw new ExceptionInInitializerError(e); 071 } 072 } 073 074 protected static final String WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; 075 076 protected static final QName WSSE_SECURITY = new QName(WSSE_NS, "Security"); 077 078 protected static final QName WSSE_USERNAME_TOKEN = new QName(WSSE_NS, "UsernameToken"); 079 080 protected static final QName WSSE_PASSWORD = new QName(WSSE_NS, "Password"); 081 082 protected static final Set<QName> HEADERS = new HashSet<QName>(); 083 084 static { 085 HEADERS.add(WSSE_SECURITY); 086 } 087 088 @Override 089 public Set<QName> getHeaders() { 090 return HEADERS; 091 } 092 093 @Override 094 public void close(MessageContext context) { 095 } 096 097 @Override 098 public boolean handleFault(SOAPMessageContext context) { 099 return true; 100 } 101 102 @Override 103 @SuppressWarnings("unchecked") 104 public boolean handleMessage(SOAPMessageContext context) { 105 if ((Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) { 106 // we are only looking at inbound messages 107 return true; 108 } 109 110 Map<String, String> callContextMap = null; 111 112 Object[] secHeaders = context.getHeaders(WSSE_SECURITY, WSSE_CONTEXT, true); 113 if (secHeaders != null && secHeaders.length > 0) { 114 for (Object header : secHeaders) { 115 if (!(header instanceof JAXBElement)) { 116 continue; 117 } 118 119 if (!(((JAXBElement<?>) header).getValue() instanceof SecurityHeaderType)) { 120 continue; 121 } 122 123 callContextMap = extractUsernamePassword((JAXBElement<SecurityHeaderType>) header); 124 if (callContextMap != null) { 125 break; 126 } 127 } 128 } 129 130 // add user and password to context 131 if (callContextMap == null) { 132 callContextMap = new HashMap<String, String>(); 133 } 134 135 context.put(AbstractService.CALL_CONTEXT_MAP, callContextMap); 136 context.setScope(AbstractService.CALL_CONTEXT_MAP, MessageContext.Scope.APPLICATION); 137 138 return true; 139 } 140 141 @SuppressWarnings("unchecked") 142 protected Map<String, String> extractUsernamePassword(JAXBElement<SecurityHeaderType> sht) { 143 String username = null; 144 String password = null; 145 for (Object uno : sht.getValue().getAny()) { 146 if ((uno instanceof JAXBElement) && ((JAXBElement<?>) uno).getValue() instanceof UsernameTokenType) { 147 UsernameTokenType utt = ((JAXBElement<UsernameTokenType>) uno).getValue(); 148 username = utt.getUsername().getValue(); 149 150 for (Object po : utt.getAny()) { 151 if ((po instanceof JAXBElement) && ((JAXBElement<?>) po).getValue() instanceof PasswordString) { 152 password = ((JAXBElement<PasswordString>) po).getValue().getValue(); 153 break; 154 } 155 } 156 157 break; 158 } 159 } 160 Map<String, String> result = null; 161 if (username != null) { 162 result = new HashMap<String, String>(); 163 result.put(CallContext.USERNAME, username); 164 result.put(CallContext.PASSWORD, password); 165 } 166 return result; 167 } 168 169 // --- JAXB classes --- 170 171 @XmlRegistry 172 public static class ObjectFactory { 173 174 public ObjectFactory() { 175 } 176 177 public SecurityHeaderType createSecurityHeaderType() { 178 return new SecurityHeaderType(); 179 } 180 181 public UsernameTokenType createUsernameTokenType() { 182 return new UsernameTokenType(); 183 } 184 185 public PasswordString createPasswordString() { 186 return new PasswordString(); 187 } 188 189 public AttributedString createAttributedString() { 190 return new AttributedString(); 191 } 192 193 @XmlElementDecl(namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", name = "Security") 194 public JAXBElement<SecurityHeaderType> createSecurity(SecurityHeaderType value) { 195 return new JAXBElement<SecurityHeaderType>(WSSE_SECURITY, SecurityHeaderType.class, null, value); 196 } 197 198 @XmlElementDecl(namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", name = "UsernameToken") 199 public JAXBElement<UsernameTokenType> createUsernameToken(UsernameTokenType value) { 200 return new JAXBElement<UsernameTokenType>(WSSE_USERNAME_TOKEN, UsernameTokenType.class, null, value); 201 } 202 203 @XmlElementDecl(namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", name = "Password") 204 public JAXBElement<PasswordString> createPassword(PasswordString value) { 205 return new JAXBElement<PasswordString>(WSSE_PASSWORD, PasswordString.class, null, value); 206 } 207 208 } 209 210 @XmlAccessorType(XmlAccessType.FIELD) 211 @XmlType(name = "SecurityHeaderType", propOrder = { "any" }) 212 public static class SecurityHeaderType { 213 214 @XmlAnyElement(lax = true) 215 protected List<Object> any; 216 @XmlAnyAttribute 217 private final Map<QName, String> otherAttributes = new HashMap<QName, String>(); 218 219 public List<Object> getAny() { 220 if (any == null) { 221 any = new ArrayList<Object>(); 222 } 223 return this.any; 224 } 225 226 public Map<QName, String> getOtherAttributes() { 227 return otherAttributes; 228 } 229 230 } 231 232 @XmlAccessorType(XmlAccessType.FIELD) 233 @XmlType(name = "UsernameTokenType", propOrder = { "username", "any" }) 234 public static class UsernameTokenType { 235 236 @XmlElement(name = "Username", namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", required = true) 237 protected AttributedString username; 238 @XmlAnyElement(lax = true) 239 protected List<Object> any; 240 @XmlAttribute(name = "Id", namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd") 241 @XmlJavaTypeAdapter(CollapsedStringAdapter.class) 242 @XmlID 243 @XmlSchemaType(name = "ID") 244 protected String id; 245 @XmlAnyAttribute 246 private final Map<QName, String> otherAttributes = new HashMap<QName, String>(); 247 248 public AttributedString getUsername() { 249 return username; 250 } 251 252 public void setUsername(AttributedString value) { 253 this.username = value; 254 } 255 256 public List<Object> getAny() { 257 if (any == null) { 258 any = new ArrayList<Object>(); 259 } 260 return this.any; 261 } 262 263 public String getId() { 264 return id; 265 } 266 267 public void setId(String value) { 268 this.id = value; 269 } 270 271 public Map<QName, String> getOtherAttributes() { 272 return otherAttributes; 273 } 274 } 275 276 @XmlAccessorType(XmlAccessType.FIELD) 277 @XmlType(name = "PasswordString") 278 public static class PasswordString extends AttributedString { 279 280 @XmlAttribute(name = "Type") 281 @XmlSchemaType(name = "anyURI") 282 protected String type; 283 284 public String getType() { 285 return type; 286 } 287 288 public void setType(String value) { 289 this.type = value; 290 } 291 } 292 293 @XmlAccessorType(XmlAccessType.FIELD) 294 @XmlType(name = "AttributedString", propOrder = { "value" }) 295 @XmlSeeAlso({ PasswordString.class }) 296 public static class AttributedString { 297 298 @XmlValue 299 protected String value; 300 @XmlAttribute(name = "Id", namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd") 301 @XmlJavaTypeAdapter(CollapsedStringAdapter.class) 302 @XmlID 303 @XmlSchemaType(name = "ID") 304 protected String id; 305 @XmlAnyAttribute 306 private final Map<QName, String> otherAttributes = new HashMap<QName, String>(); 307 308 public String getValue() { 309 return value; 310 } 311 312 public void setValue(String value) { 313 this.value = value; 314 } 315 316 public String getId() { 317 return id; 318 } 319 320 public void setId(String value) { 321 this.id = value; 322 } 323 324 public Map<QName, String> getOtherAttributes() { 325 return otherAttributes; 326 } 327 } 328 329}