package elw.webauth;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.io.CharStreams;
import elw.web.core.W;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.openid4java.association.AssociationException;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.discovery.yadis.YadisResolver;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.MessageException;
import org.openid4java.message.ParameterList;
import org.openid4java.message.ax.AxMessage;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.message.ax.FetchResponse;
import org.openid4java.server.RealmVerifier;
import org.openid4java.server.RealmVerifierFactory;
import org.openid4java.util.HttpFetcherFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.mail.*;
import javax.mail.Message;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
public abstract class ControllerAuth {
private static final Logger log = LoggerFactory.getLogger(ControllerAuth.class);
public static final String SESSION_SUCCESS_REDIRECT = "SUCCESS_REDIRECT";
protected static final String SESSION_OID_DISCOVERY = "OID_DISCOVERY";
protected static final String SESSION_OID_CONSUMER = "OID_CONSUMER";
protected static final Pattern OID_YAHOO =
Pattern.compile("^https?://me\\.yahoo\\.com(/.*)?$");
protected static final Pattern OID_GOOGLE =
Pattern.compile("^https?://www\\.google\\.com/accounts/o8/id(/.*)?(\\?.+)?$");
protected static final Pattern OID_GOOGLE_PROFILES =
Pattern.compile("^https?://www\\.google\\.com/profiles/.+$");
protected static final Pattern OID_YANDEX =
Pattern.compile("^https?://openid\\.yandex\\.ru(/.*)?$");
protected static final Pattern OID_MAILRU =
Pattern.compile("^https?://openid\\.mail\\.ru/mail(/.*)?$");
protected static final String OID_REDIR_BODY_TARGETURL = "${targetUrl}";
protected static final String OID_REDIR_BODY_PARAMS = "${paramsGoHere}";
protected static final String OID_REDIR_PARAMKEY = "${parameter.key}";
protected static final String OID_REDIR_PARAMVAL = "${parameter.value}";
/**
* Not all endpoints are reporting confirmed emails (i.e. myopenid reports an unconfirmed email thus forging it),
* so we have to white-list (or, rather white-map) both openids and corresponding emails.
*/
protected static final Map<Pattern, Pattern> OID_EMAIL_WHITEMAP =
Collections.unmodifiableMap(
createTrustedProviderMap()
);
protected static Map<Pattern, Pattern> createTrustedProviderMap() {
final Map<Pattern, Pattern> trustedProviders =
new HashMap<Pattern, Pattern>();
trustedProviders.put(OID_YAHOO, Pattern.compile("^.+@yahoo\\.com$"));
trustedProviders.put(OID_GOOGLE, Pattern.compile("^.+@(gmail\\.com|akraievoy\\.org)$"));
trustedProviders.put(OID_GOOGLE_PROFILES, Pattern.compile("^.+@(gmail\\.com|akraievoy\\.org)$"));
trustedProviders.put(OID_YANDEX, Pattern.compile("^.+@yandex\\.ru$"));
trustedProviders.put(OID_MAILRU, Pattern.compile("^.+@mail\\.ru$"));
return trustedProviders;
}
protected static RealmVerifierFactory realmVerifierFactory =
// what the heck, I did not see anything like that in the docs?..
new RealmVerifierFactory(
new YadisResolver(
new HttpFetcherFactory()
)
);
// the most simple way to get base64 of binary hash (no additional libs)
private final ObjectMapper mapper = new ObjectMapper();
private final ServerConfigAuth serverConfig;
private final Cache<String, Long> srcAddrToStamp;
public ControllerAuth(ServerConfigAuth serverConfig) {
this.serverConfig = serverConfig;
Security.addProvider(new BouncyCastleProvider());
final CacheBuilder<Object, Object> caches =
CacheBuilder.newBuilder()
.concurrencyLevel(3)
.expireAfterWrite(
2 * serverConfig.getMailSourceDelayMillis(),
TimeUnit.MILLISECONDS
);
this.srcAddrToStamp = caches.build();
}
public static void storeSuccessRedirect(
HttpServletRequest req
) {
final String reqUri = req.getRequestURI();
if (!Strings.isNullOrEmpty(req.getQueryString())) {
req.getSession(true).setAttribute(
SESSION_SUCCESS_REDIRECT,
reqUri + "?" + req.getQueryString()
);
} else {
req.getSession(true).setAttribute(
SESSION_SUCCESS_REDIRECT,
reqUri
);
}
}
@RequestMapping(
value = "smtpchallenge",
method = RequestMethod.GET
)
public ModelAndView do_smtpchallengeGet(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
return do_smtpchallengePost(req, resp);
}
@RequestMapping(
value = "smtpchallenge",
method = RequestMethod.POST
)
public ModelAndView do_smtpchallengePost(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
final String emailParam = req.getParameter("email");
if (emailParam == null || emailParam.trim().length() == 0) {
resp.sendError(
HttpServletResponse.SC_BAD_REQUEST,
"email parameter not set"
);
return null;
}
final String remoteAddress = W.resolveRemoteAddress(req);
final long forcedDelayMillis =
serverConfig.getMailSourceDelayMillis();
final Long lastStamp = srcAddrToStamp.getIfPresent(remoteAddress);
final long currentMillis = System.currentTimeMillis();
if (lastStamp != null &&
lastStamp + forcedDelayMillis > currentMillis) {
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
"your IP is requesting smtp auth too often"
);
return null;
}
srcAddrToStamp.put(remoteAddress, currentMillis);
final String email = emailParam.trim().toLowerCase();
final String smtpChallengeToken;
try {
smtpChallengeToken = registerChallengeToken(req, email);
} catch (AuthException e) {
log.warn("failed on smtpchallenge", e);
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
e.getMessage()
);
return null;
}
final Session session = setupSmtpSession();
try {
Message message = createMessage(
session, req, email, smtpChallengeToken
);
final Transport transport =
session.getTransport(serverConfig.getMailProtocol());
transport.connect(
serverConfig.getSmtpHost(),
serverConfig.getSmtpPort(),
serverConfig.getSmtpUser(),
serverConfig.getSmtpPass()
);
transport.sendMessage(
message,
message.getRecipients(Message.RecipientType.TO)
);
} catch (MessagingException e) {
log.warn("failed on smtpchallenge", e);
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
e.getMessage()
);
return null;
}
final StringBuilder responseBuilder =
generateSmtpResponseBody(email);
sendBody(resp, responseBuilder);
return null;
}
protected StringBuilder generateSmtpResponseBody(final String email) {
final StringBuilder responseBuilder =
new StringBuilder(serverConfig.getMailResponseForm());
replace(
responseBuilder,
"${targetUrl}",
serverConfig.getBaseUrl() + "auth/smtpresponse"
);
replace(
responseBuilder,
"${email}",
email
);
return responseBuilder;
}
protected Message createMessage(
final Session session, final HttpServletRequest req,
final String emailAddress,
final String smtpChallengeToken
) throws MessagingException, UnsupportedEncodingException {
final String mailBody =
generateMailBody(req, emailAddress, smtpChallengeToken);
final Message message = new MimeMessage(session);
message.setSentDate(new Date());
message.setFrom(new InternetAddress(serverConfig.getSmtpFrom()));
message.setRecipients(
Message.RecipientType.TO,
InternetAddress.parse(emailAddress)
);
message.setSubject(serverConfig.getSmtpSubject());
message.setText( mailBody);
return message;
}
protected String generateMailBody(
final HttpServletRequest req,
final String emailAddress,
final String smtpChallengeToken
) throws UnsupportedEncodingException {
final StringBuilder mailBodyBuilder =
new StringBuilder(serverConfig.getMailBody());
replace(
mailBodyBuilder,
"${smtpAuth.sourceAddr}",
W.resolveRemoteAddress(req)
);
replace(
mailBodyBuilder,
"${smtpAuth.token}",
smtpChallengeToken
);
replace(
mailBodyBuilder,
"${smtpAuth.responseUrl}",
serverConfig.getBaseUrl() +
"auth/smtpresponse?" +
"email=" + URLEncoder.encode(emailAddress, "UTF-8") + "&" +
"token=" + URLEncoder.encode(smtpChallengeToken, "UTF-8")
);
return mailBodyBuilder.toString();
}
protected abstract String registerChallengeToken(
final HttpServletRequest req, String emailAddress
) throws AuthException;
protected String challengeToken(
final String email,
final Long stamp,
final String sessionId
) throws AuthException {
final String superSecretSalt = serverConfig.getMailTokenSalt();
try {
final MessageDigest mda = MessageDigest.getInstance(
"SHA-1",
"BC"
);
final String challengeData =
email + " " +
stamp + " " +
sessionId + " " +
superSecretSalt;
final byte[] digest =
mda.digest(challengeData.getBytes());
// drop some bytes to avoid padding char
final byte[] digestPadded =
new byte[digest.length - digest.length % 3];
System.arraycopy(digest, 0, digestPadded, 0, digestPadded.length);
final String base64Quoted =
mapper.writeValueAsString(digestPadded);
final String base64 =
base64Quoted.substring(1, base64Quoted.length() - 1);
// switch to URL-safe variant
return base64.replace("+", "-").replace("/", "_");
} catch (NoSuchAlgorithmException e) {
throw new AuthException("BouncyCastle/SHA-1'd been abducted", e);
} catch (java.security.NoSuchProviderException e) {
throw new AuthException("BouncyCastle took off and flew away", e);
} catch (JsonMappingException e) {
throw new AuthException("Jackson doesn't", e);
} catch (JsonGenerationException e) {
throw new AuthException("Jackson didn't", e);
} catch (IOException e) {
throw new AuthException("Jackson would rather not", e);
}
}
protected Session setupSmtpSession() {
final Properties props = new Properties();
props.put(
"mail.smtp.auth",
serverConfig.getSmtpAuth()
);
props.put(
"mail.smtp.starttls.enable",
String.valueOf(serverConfig.getSmtpStartTls())
);
props.put(
"mail.smtp.host",
serverConfig.getSmtpHost()
);
props.put(
"mail.smtp.port",
serverConfig.getSmtpPort()
);
props.put(
"mail.smtp.socketFactory.port",
serverConfig.getSmtpPort()
);
props.put(
"mail.smtp.socketFactory.class",
serverConfig.getSmtpSocketFactory()
);
props.put(
"mail.smtp.socketFactory.fallback",
"false"
);
final Session session = Session.getInstance(
props,
new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
serverConfig.getSmtpUser(),
serverConfig.getSmtpPass()
);
}
}
);
session.setDebug(serverConfig.isSmtpDebug());
return session;
}
@RequestMapping(
value = "smtpresponse",
method = RequestMethod.GET
)
public ModelAndView do_smtpresponseGet(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
return do_smtpresponsePost(req, resp);
}
@RequestMapping(
value = "smtpresponse",
method = RequestMethod.POST
)
public ModelAndView do_smtpresponsePost(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
final String emailParam = req.getParameter("email");
if (emailParam == null || emailParam.trim().length() == 0) {
resp.sendError(
HttpServletResponse.SC_BAD_REQUEST,
"email parameter not set"
);
return null;
}
final String email = emailParam.trim().toLowerCase();
final String tokenParam = req.getParameter("token");
if (tokenParam == null || tokenParam.trim().length() == 0) {
resp.sendError(
HttpServletResponse.SC_BAD_REQUEST,
"tokenParam parameter not set"
);
return null;
}
final String token = tokenParam.trim();
try {
if (!activateAuth(req, email, token)) {
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
"failed to activate token"
);
return null;
}
} catch (AuthException e) {
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
"failed to activate token: " + e.getMessage()
);
return null;
}
processAuth(
req,
resp,
req.getSession(true),
Collections.singletonList(email),
Collections.<String>emptyList()
);
return null;
}
protected abstract boolean activateAuth(
final HttpServletRequest req,
final String email,
final String token
) throws AuthException;
@RequestMapping(
value = "oidchallenge",
method = RequestMethod.GET
)
public ModelAndView do_oidchallengeGet(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
final String oidIdent = req.getParameter("openid_identifier");
if (oidIdent == null || oidIdent.trim().length() == 0) {
resp.sendError(
HttpServletResponse.SC_BAD_REQUEST,
"openid_identifier not set"
);
return null;
}
final OpenIdChallengeResult challengeResult;
try {
challengeResult =
oidchallenge(
req,
oidIdent,
serverConfig.getBaseUrl() + "auth/oidresponse"
);
} catch (AuthException e) {
log.warn("failed on oidchallenge", e);
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
e.getMessage()
);
return null;
}
if (challengeResult.method == HttpMethod.GET) {
resp.sendRedirect(challengeResult.targetUrl);
return null;
}
final StringBuilder responseBody =
generateFormRedirectBody(challengeResult);
sendBody(resp, responseBody);
return null;
}
protected void sendBody(
final HttpServletResponse resp,
final StringBuilder responseBody
) throws IOException {
resp.setContentType("text/html; charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", System.currentTimeMillis());
CharStreams.copy(
new StringReader(responseBody.toString()),
resp.getWriter()
);
}
protected StringBuilder generateFormRedirectBody(
OpenIdChallengeResult challengeResult
) {
final StringBuilder params = new StringBuilder();
for (Object key : challengeResult.parameterMap.keySet()) {
params.append(serverConfig.getFormRedirectParam());
replace(
params,
OID_REDIR_PARAMKEY,
String.valueOf(key)
);
replace(
params,
OID_REDIR_PARAMVAL,
String.valueOf(challengeResult.parameterMap.get(key))
);
}
final StringBuilder responseBody =
new StringBuilder(serverConfig.getFormRedirect());
replace(
responseBody,
OID_REDIR_BODY_TARGETURL,
challengeResult.targetUrl
);
replace(
responseBody,
OID_REDIR_BODY_PARAMS,
params.toString()
);
return responseBody;
}
protected static StringBuilder replace(
final StringBuilder content,
final String search,
final String replace
) {
int replaces = 16;
int targetIndex;
while (replaces-- > 0 && (targetIndex = content.indexOf(search)) >= 0) {
content.replace(
targetIndex,
targetIndex + search.length(),
replace
);
}
return content;
}
@RequestMapping(
value = "oidresponse",
method = RequestMethod.GET
)
public ModelAndView do_oidresponseGet(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
return do_oidresponsePost(req, resp);
}
@RequestMapping(
value = "oidresponse",
method = RequestMethod.POST
)
public ModelAndView do_oidresponsePost(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException {
try {
final HttpSession session = req.getSession(true);
final OpenIdResponseResult openIdResponseResult =
oidresponse(req, session);
final List<String> emails = openIdResponseResult.emails;
final List<String> openIds = openIdResponseResult.openIds;
return processAuth(req, resp, session, emails, openIds);
} catch (AuthException e) {
log.warn("failed on oidresponse", e);
resp.sendError(
HttpServletResponse.SC_SERVICE_UNAVAILABLE,
e.getMessage()
);
return null;
}
}
protected ModelAndView processAuth(
final HttpServletRequest req,
final HttpServletResponse resp,
final HttpSession session,
final List<String> emails,
final List<String> openIds
) throws IOException {
processAuthInfo(req, session, emails, openIds);
return processAuthSuccess(req, resp);
}
protected ModelAndView processAuthSuccess(
HttpServletRequest req,
HttpServletResponse resp
) throws IOException {
final HttpSession session = req.getSession(true);
final Object successRedirSession =
session.getAttribute(SESSION_SUCCESS_REDIRECT);
final String successRedir;
if (successRedirSession == null) {
successRedir = serverConfig.getBaseUrl();
} else {
successRedir = String.valueOf(successRedirSession);
}
resp.sendRedirect(successRedir);
session.removeAttribute(SESSION_SUCCESS_REDIRECT);
return null;
}
protected abstract void processAuthInfo(
HttpServletRequest req,
HttpSession session,
List<String> emails,
List<String> openIds
);
public OpenIdChallengeResult oidchallenge(
final HttpServletRequest request,
final String oidIdent,
final String responseUrl
) throws AuthException {
// adding some extra code for exception handling
try {
return oidChallenge_unsafe(request, oidIdent, responseUrl);
} catch (DiscoveryException e) {
throw new AuthException("OpenID auth failed", e);
} catch (MessageException e) {
throw new AuthException("OpenID auth failed", e);
} catch (ConsumerException e) {
throw new AuthException("OpenID auth failed", e);
}
}
protected OpenIdChallengeResult oidChallenge_unsafe(
final HttpServletRequest request,
final String oidIdent,
final String responseUrl
) throws DiscoveryException, MessageException, ConsumerException {
final HttpSession session = request.getSession(true);
// coding this up this just like the QuickStart sample says, see:
// http://code.google.com/p/openid4java/wiki/QuickStart
// perform discovery on the user-supplied identifier
final ConsumerManager oidConsumer = oidConsumer(session, true);
final List discoveries = oidConsumer.discover(oidIdent);
// attempt to associate with the OpenID provider
// and retrieve one service endpoint for authentication
final DiscoveryInformation discovered =
oidConsumer.associate(discoveries);
// store the discovery information in the user's session for later use
// leave out for stateless operation / if there is no session
session.setAttribute(SESSION_OID_DISCOVERY, discovered);
// obtain a AuthRequest message to be sent to the OpenID provider
final AuthRequest authReq =
oidConsumer.authenticate(
discovered,
responseUrl
);
FetchRequest fetch = FetchRequest.createFetchRequest();
if (OID_GOOGLE.matcher(oidIdent).matches() ||
OID_YAHOO.matcher(oidIdent).matches() ||
OID_YANDEX.matcher(oidIdent).matches()) {
fetch.addAttribute(
"email",
// this is not online any more. They say it will be replaced \
// with http://openid.net/schema/...
"http://axschema.org/contact/email",
true
);
} else {
// TODO works for myOpenID, but should this be the default case?
fetch.addAttribute(
"email",
//this is not online either
"http://schema.openid.net/contact/email",
true
);
}
if (!fetch.getAttributes().isEmpty()) {
authReq.addExtension(fetch);
}
if (discovered.isVersion2()) {
return new OpenIdChallengeResult(
HttpMethod.POST,
authReq.getDestinationUrl(false),
authReq.getParameterMap()
);
}
return new OpenIdChallengeResult(
HttpMethod.GET,
authReq.getDestinationUrl(true),
Collections.EMPTY_MAP
);
}
public OpenIdResponseResult oidresponse(
final HttpServletRequest request,
final HttpSession session
) throws AuthException {
final ConsumerManager oidConsumer = oidConsumer(session, false);
if (oidConsumer == null) {
throw new AuthException(
"no ConsumerManager in session", new IllegalStateException()
);
}
try {
return oidresponse_unsafe(request, session, oidConsumer);
} catch (MessageException e) {
throw new AuthException("OpenID auth failed", e);
} catch (DiscoveryException e) {
throw new AuthException("OpenID auth failed", e);
} catch (AssociationException e) {
throw new AuthException("OpenID auth failed", e);
} finally {
session.removeAttribute(SESSION_OID_DISCOVERY);
session.removeAttribute(SESSION_OID_CONSUMER);
}
}
protected OpenIdResponseResult oidresponse_unsafe(
final HttpServletRequest request,
final HttpSession session,
final ConsumerManager oidConsumer
) throws MessageException, DiscoveryException,
AssociationException, AuthException {
// extract the parameters from the authentication response
// (which comes in as a HTTP request from the OpenID provider)
final ParameterList openidResp =
new ParameterList(request.getParameterMap());
// retrieve the previously stored discovery information
final DiscoveryInformation discovered =
(DiscoveryInformation) session.getAttribute(SESSION_OID_DISCOVERY);
// extract the receiving URL from the HTTP request
final StringBuffer receivingURL = request.getRequestURL();
final String queryString = request.getQueryString();
if (queryString != null && queryString.length() > 0)
receivingURL.append("?").append(request.getQueryString());
// verify the response
final VerificationResult verification = oidConsumer.verify(
receivingURL.toString(),
openidResp,
discovered
);
// examine the verification result and extract the verified identifier
final Identifier verified = verification.getVerifiedId();
if (verified == null) {
throw new AuthException(
"OpenID authentication failed", new IllegalStateException()
);
}
final List<String> emails = new ArrayList<String>();
final AuthSuccess authSuccess =
(AuthSuccess) verification.getAuthResponse();
if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
final FetchResponse fetchResp =
(FetchResponse) authSuccess.getExtension(
AxMessage.OPENID_NS_AX
);
List emailsRaw = fetchResp.getAttributeValues("email");
for (Object emailRaw : emailsRaw) {
emails.add(String.valueOf(emailRaw).trim().toLowerCase());
}
}
Pattern trustedEmails = null;
for (Map.Entry<Pattern, Pattern> trustedEndpoint : OID_EMAIL_WHITEMAP.entrySet()) {
if (trustedEndpoint.getKey().matcher(authSuccess.getClaimed()).matches()) {
trustedEmails = trustedEndpoint.getValue();
}
}
// success
final List<String> emailsEffective =
new ArrayList<String>();
if (trustedEmails != null) {
for (String email : emails) {
if (trustedEmails.matcher(email).matches()) {
emailsEffective.add(email);
} else {
log.warn("claimed email {} does not match trustedEmails pattern: {}", email, trustedEmails);
}
}
} else {
log.warn("ignoring emails: {}", emails);
}
return new OpenIdResponseResult(
emailsEffective,
Collections.singletonList(verified.getIdentifier())
);
}
protected ConsumerManager oidConsumer(
final HttpSession session,
final boolean create
) {
final Object existing = session.getAttribute(SESSION_OID_CONSUMER);
if (existing != null || !create) {
return (ConsumerManager) existing;
}
final ConsumerManager created = new ConsumerManager();
if (created.getRealmVerifier() == null) {
RealmVerifier rv = realmVerifierFactory.getRealmVerifierForConsumer();
rv.setEnforceRpId(serverConfig.isRelyingPartyIdent());
created.setRealmVerifier(rv);
} else {
created.getRealmVerifier().setEnforceRpId(
serverConfig.isRelyingPartyIdent()
);
}
session.setAttribute(SESSION_OID_CONSUMER, created);
return created;
}
}