diff options
Diffstat (limited to 'id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol')
6 files changed, 1087 insertions, 0 deletions
diff --git a/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20AuthAction.java b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20AuthAction.java new file mode 100644 index 000000000..d90df51e7 --- /dev/null +++ b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20AuthAction.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +package at.gv.egovernment.moa.id.protocols.oauth20.protocol; + +import java.security.SignatureException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; +import at.gv.egovernment.moa.id.advancedlogging.MOAReversionLogger; +import at.gv.egovernment.moa.id.auth.exception.MOAIDException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.data.SLOInformationImpl; +import at.gv.egovernment.moa.id.data.SLOInformationInterface; +import at.gv.egovernment.moa.id.moduls.IAction; +import at.gv.egovernment.moa.id.moduls.IRequest; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20SessionObject; +import at.gv.egovernment.moa.id.protocols.oauth20.Pair; +import at.gv.egovernment.moa.id.protocols.oauth20.attributes.OAuth20AttributeBuilder; +import at.gv.egovernment.moa.id.protocols.oauth20.attributes.OpenIdExpirationTimeAttribute; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20Exception; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20ResponseTypeException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20ServerErrorException; +import at.gv.egovernment.moa.id.protocols.oauth20.json.OAuth20SignatureUtil; +import at.gv.egovernment.moa.id.protocols.oauth20.json.OAuthJsonToken; +import at.gv.egovernment.moa.id.protocols.oauth20.json.OAuthSigner; +import at.gv.egovernment.moa.id.storage.AssertionStorage; +import at.gv.egovernment.moa.id.util.Random; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; + +class OAuth20AuthAction implements IAction { + + public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, + IAuthData authData) throws MOAIDException { + + OAuth20AuthRequest oAuthRequest = (OAuth20AuthRequest) req; + String responseType = oAuthRequest.getResponseType(); + + MOAReversionLogger.getInstance().logEvent(req, MOAIDEventConstants.AUTHPROTOCOL_OPENIDCONNECT_AUTHREQUEST); + + String code = Random.nextRandom(); + + try { + + String accessToken = UUID.randomUUID().toString(); + + Logger.debug("Stored session with id: " + code); + OAuth20SessionObject o = new OAuth20SessionObject(); + if (responseType.equals(OAuth20Constants.RESPONSE_CODE)) { + o.setScope(oAuthRequest.getScope()); + o.setCode(code); + + //generate idToken from MOASession + Map<String, Object> idToken = generateIDToken(o, oAuthRequest, authData, accessToken); + o.setAuthDataSession(idToken); + + } else if (responseType.equals(OAuth20Constants.RESPONSE_TOKEN)) { + throw new OAuth20ResponseTypeException(); + } + + // store data in oath session + AssertionStorage.getInstance().put(code, o); + + Logger.debug("Saved OAuth20SessionObject in session with id: " + code); + + // add code and state to redirect url + httpResp.setStatus(HttpServletResponse.SC_FOUND); + String redirectURI = oAuthRequest.getRedirectUri(); + String state = oAuthRequest.getState(); + + redirectURI = this.addURLParameter(redirectURI, OAuth20Constants.RESPONSE_CODE, code); + redirectURI = this.addURLParameter(redirectURI, OAuth20Constants.PARAM_STATE, state); + + String finalUrl = redirectURI; + httpResp.addHeader("Location", finalUrl); + Logger.debug("REDIRECT TO: " + finalUrl.toString()); + + + //TODO: maybe add bPK / wbPK to SLO information + SLOInformationInterface sloInformation = new SLOInformationImpl(accessToken, null, null, req.requestedModule()); + + return sloInformation; + } + catch (Exception e) { + + //remove OAuthSessionObject if it already exists + if (AssertionStorage.getInstance().containsKey(code)) { + AssertionStorage.getInstance().remove(code); + } + + if (e instanceof OAuth20Exception) { + throw (OAuth20Exception) e; + } + throw new OAuth20ServerErrorException(); + } + + } + + private Map<String, Object> generateIDToken(OAuth20SessionObject auth20SessionObject, + OAuth20AuthRequest oAuthRequest, IAuthData authData, String accessToken) throws SignatureException, MOAIDException { + + // create response + Map<String, Object> params = new HashMap<String, Object>(); + params.put(OAuth20Constants.RESPONSE_ACCESS_TOKEN, accessToken); + params.put(OAuth20Constants.RESPONSE_TOKEN_TYPE, OAuth20Constants.RESPONSE_TOKEN_TYPE_VALUE_BEARER); + params.put(OAuth20Constants.RESPONSE_EXPIRES_IN, OpenIdExpirationTimeAttribute.expirationTime); + // build id token and scope + Pair<String, String> pair = buildIdToken(auth20SessionObject.getScope(), oAuthRequest, + authData); + Logger.debug("RESPONSE ID_TOKEN: " + pair.getFirst()); + params.put(OAuth20Constants.RESPONSE_ID_TOKEN, pair.getFirst()); + Logger.debug("RESPONSE SCOPE: " + pair.getSecond()); + params.put(OAuth20Constants.PARAM_SCOPE, pair.getSecond()); + + return params; + + } + + private Pair<String, String> buildIdToken(String scope, OAuth20AuthRequest oAuthRequest, IAuthData authData) + throws MOAIDException, SignatureException { + OAAuthParameter oaParam = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(oAuthRequest.getOAURL()); + + OAuthSigner signer = OAuth20SignatureUtil.loadSigner(authData.getIssuer()); + OAuthJsonToken token = new OAuthJsonToken(signer); + + StringBuilder resultScopes = new StringBuilder(); + // always fill with open id + OAuth20AttributeBuilder.addScopeOpenId(token.getPayloadAsJsonObject(), oaParam, authData, oAuthRequest); + resultScopes.append("openId"); + + for (String s : scope.split(" ")) { + if (s.equalsIgnoreCase("profile")) { + OAuth20AttributeBuilder.addScopeProfile(token.getPayloadAsJsonObject(), oaParam, authData); + resultScopes.append(" profile"); + } else if (s.equalsIgnoreCase("eID")) { + OAuth20AttributeBuilder.addScopeEID(token.getPayloadAsJsonObject(), oaParam, authData); + resultScopes.append(" eID"); + } else if (s.equalsIgnoreCase("eID_gov")) { + OAuth20AttributeBuilder.addScopeEIDGov(token.getPayloadAsJsonObject(), oaParam, authData); + resultScopes.append(" eID_gov"); + } else if (s.equalsIgnoreCase("mandate")) { + OAuth20AttributeBuilder.addScopeMandate(token.getPayloadAsJsonObject(), oaParam, authData); + resultScopes.append(" mandate"); + } else if (s.equalsIgnoreCase("stork")) { + OAuth20AttributeBuilder.addScopeSTORK(token.getPayloadAsJsonObject(), oaParam, authData); + resultScopes.append(" stork"); + } + } + + // add properties and sign + // HmacSHA256Signer signer = new HmacSHA256Signer("testSigner", "key_id", + // "super_secure_pwd".getBytes()); + // Signer signer = OAuth20Util.loadSigner(authData.getIssuer(), oaParam.getoAuth20Config()); + + return Pair.newInstance(token.serializeAndSign(), resultScopes.toString()); + } + + /* + * (non-Javadoc) + * @see + * at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls + * .IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { + return true; + } + + private String addURLParameter(String url, String name, String value) { + String param = name + "=" + value; + if (url.indexOf("?") < 0) { + return url + "?" + param; + } else { + return url + "&" + param; + } + } + + /* + * (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() + */ + public String getDefaultActionName() { + return OAuth20Protocol.AUTH_ACTION; + } + +} diff --git a/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20AuthRequest.java b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20AuthRequest.java new file mode 100644 index 000000000..06509b333 --- /dev/null +++ b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20AuthRequest.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +package at.gv.egovernment.moa.id.protocols.oauth20.protocol; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.opensaml.saml2.core.Attribute; + +import at.gv.egovernment.moa.id.commons.config.MOAIDConfigurationConstants; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Util; +import at.gv.egovernment.moa.id.protocols.oauth20.attributes.OAuth20AttributeBuilder; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20AccessDeniedException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20Exception; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20ResponseTypeException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20WrongParameterException; +import at.gv.egovernment.moa.id.protocols.pvp2x.PVP2XProtocol; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.AttributQueryBuilder; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.attributes.IAttributeBuilder; +import at.gv.egovernment.moa.logging.Logger; + +public class OAuth20AuthRequest extends OAuth20BaseRequest { + + private static final long serialVersionUID = 1L; + + private String responseType; + private String state; + private String redirectUri; + private String scope; + private String clientID; + private String nonce; + + /** + * @return the responseType + */ + public String getResponseType() { + return responseType; + } + + /** + * @param responseType + * the responseType to set + */ + public void setResponseType(String responseType) { + this.responseType = responseType; + } + + /** + * @return the state + */ + public String getState() { + return state; + } + + /** + * @param state + * the state to set + */ + public void setState(String state) { + this.state = state; + } + + /** + * @return the redirectUri + */ + public String getRedirectUri() { + return redirectUri; + } + + /** + * @param redirectUri + * the redirectUri to set + */ + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + /** + * @return the scope + */ + public String getScope() { + return scope; + } + + /** + * @param scope + * the scope to set + */ + public void setScope(String scope) { + this.scope = scope; + } + + /** + * @return the clientID + */ + public String getClientID() { + return clientID; + } + + /** + * @param clientID + * the clientID to set + */ + public void setClientID(String clientID) { + this.clientID = clientID; + } + + + + /** + * @return the nonce + */ + public String getNonce() { + return nonce; + } + + /** + * @param nonce the nonce to set + */ + public void setNonce(String nonce) { + this.nonce = nonce; + } + + @Override + protected void populateSpecialParameters(HttpServletRequest request) throws OAuth20Exception { + this.setResponseType(this.getParam(request, OAuth20Constants.PARAM_RESPONSE_TYPE, true)); + this.setState(this.getParam(request, OAuth20Constants.PARAM_STATE, true)); + this.setRedirectUri(this.getParam(request, OAuth20Constants.PARAM_REDIRECT_URI, true)); + this.setClientID(this.getParam(request, OAuth20Constants.PARAM_CLIENT_ID, true)); + this.setScope(this.getParam(request, OAuth20Constants.PARAM_SCOPE, false)); + this.setNonce(this.getParam(request, OAuth20Constants.PARAM_NONCE, false)); + + // check for response type + if (!this.responseType.equals(OAuth20Constants.RESPONSE_CODE)) { + throw new OAuth20ResponseTypeException(); + } + + // check state for invalid characters (like < > & ; ... javascript ... to prevent xss) + if (!OAuth20Util.isValidStateValue(this.getState())) { + throw new OAuth20WrongParameterException(OAuth20Constants.PARAM_STATE); + } + + // check if client id and redirect uri are ok + try { + // OAOAUTH20 cannot be null at this point. check was done in base request + OAAuthParameter oAuthConfig = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(this.getOAURL()); + + + if (!this.getClientID().equals(oAuthConfig.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_CLIENTID)) + || !this.getRedirectUri().equals(oAuthConfig.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_REDIRECTURL))) { + throw new OAuth20AccessDeniedException(); + } + + this.setOnlineApplicationConfiguration(oAuthConfig); + Logger.info("Dispatch OpenIDConnect AuthRequest: ClientID=" + this.clientID); + + + } catch (ConfigurationException e) { + throw new OAuth20WrongParameterException(OAuth20Constants.PARAM_CLIENT_ID); + } + + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.RequestImpl#getRequestedAttributes() + */ + @Override + public List<Attribute> getRequestedAttributes() { + Map<String, String> reqAttr = new HashMap<String, String>(); + for (String el : PVP2XProtocol.DEFAULTREQUESTEDATTRFORINTERFEDERATION) + reqAttr.put(el, ""); + + try { + OAAuthParameter oa = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(getOAURL()); + + for (String s : scope.split(" ")) { + if (s.equalsIgnoreCase("profile")) { + for (IAttributeBuilder el :OAuth20AttributeBuilder.getBuildersprofile()) + reqAttr.put(el.getName(), ""); + + } else if (s.equalsIgnoreCase("eID")) { + for (IAttributeBuilder el :OAuth20AttributeBuilder.getBuilderseid()) + reqAttr.put(el.getName(), ""); + + } else if (s.equalsIgnoreCase("eID_gov")) { + for (IAttributeBuilder el :OAuth20AttributeBuilder.getBuilderseidgov()) + reqAttr.put(el.getName(), ""); + + } else if (s.equalsIgnoreCase("mandate")) { + for (IAttributeBuilder el :OAuth20AttributeBuilder.getBuildersmandate()) + reqAttr.put(el.getName(), ""); + + } else if (s.equalsIgnoreCase("stork")) { + for (IAttributeBuilder el :OAuth20AttributeBuilder.getBuildersstork()) + reqAttr.put(el.getName(), ""); + + } + } + + return AttributQueryBuilder.buildSAML2AttributeList(oa, reqAttr.keySet().iterator()); + + } catch (ConfigurationException e) { + Logger.error("Load configuration for OA " + getOAURL() + " FAILED", e); + return null; + } + } +} diff --git a/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20BaseRequest.java b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20BaseRequest.java new file mode 100644 index 000000000..bd3fdb3e8 --- /dev/null +++ b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20BaseRequest.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +package at.gv.egovernment.moa.id.protocols.oauth20.protocol; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; + +import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; +import at.gv.egovernment.moa.id.advancedlogging.MOAReversionLogger; +import at.gv.egovernment.moa.id.commons.config.MOAIDConfigurationConstants; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.moduls.RequestImpl; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20Exception; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20InvalidRequestException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20OANotSupportedException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20WrongParameterException; +import at.gv.egovernment.moa.id.util.ParamValidatorUtils; +import at.gv.egovernment.moa.logging.Logger; + +abstract class OAuth20BaseRequest extends RequestImpl { + + private static final long serialVersionUID = 1L; + + protected Set<String> allowedParameters = new HashSet<String>(); + + protected OAuth20BaseRequest() { + + } + + protected String getParam(final HttpServletRequest request, final String name, final boolean isNeeded) throws OAuth20Exception { + String param = request.getParameter(name); + Logger.debug("Reading param " + name + " from HttpServletRequest with value " + param); + + if (isNeeded && StringUtils.isEmpty(param)) { + throw new OAuth20WrongParameterException(name); + } + + this.allowedParameters.add(name); + + return param; + } + + protected void populateParameters(final HttpServletRequest request) throws OAuth20Exception { + + // moa id - load oa with client id! + try { + String oaURL = StringEscapeUtils.escapeHtml(this.getParam(request, OAuth20Constants.PARAM_CLIENT_ID, true)); + if (!ParamValidatorUtils.isValidOA(oaURL)) { + throw new OAuth20WrongParameterException(OAuth20Constants.PARAM_CLIENT_ID); + } + this.setOAURL(oaURL); + OAAuthParameter oaParam = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(oaURL); + + if (oaParam == null) { + throw new OAuth20WrongParameterException(OAuth20Constants.PARAM_CLIENT_ID); + } + this.setTarget(oaParam.getTarget()); + + if (StringUtils.isEmpty(oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_CLIENTSECRET)) + || StringUtils.isEmpty(oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_CLIENTID)) + || StringUtils.isEmpty(oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_REDIRECTURL))) { + throw new OAuth20OANotSupportedException(); + } + } + catch (ConfigurationException e) { + throw new OAuth20WrongParameterException(OAuth20Constants.PARAM_CLIENT_ID); + } + + // oAuth + this.populateSpecialParameters(request); + + // cleanup parameters + this.checkAllowedParameters(request); + } + + private void checkAllowedParameters(final HttpServletRequest request) { + Logger.debug("Going to check for allowed parameters"); + this.allowedParameters.add(OAuth20Constants.PARAM_MOA_ACTION); + this.allowedParameters.add(OAuth20Constants.PARAM_MOA_MOD); + + @SuppressWarnings("rawtypes") + Iterator iter = request.getParameterMap().keySet().iterator(); + while (iter.hasNext()) { + String name = (String) iter.next(); + if (!this.allowedParameters.contains(name)) { + + Logger.debug("Found wrong parameter: " + name); + throw new OAuth20WrongParameterException(name); + } + } + + } + + protected abstract void populateSpecialParameters(final HttpServletRequest request) throws OAuth20Exception; + + public static OAuth20BaseRequest newInstance(final String action, final HttpServletRequest request, String sessionId, String transactionId) throws OAuth20Exception { + OAuth20BaseRequest res; + + if (action.equals(OAuth20Protocol.AUTH_ACTION)) { + res = new OAuth20AuthRequest(); + + } else if (action.equals(OAuth20Protocol.TOKEN_ACTION)) { + res = new OAuth20TokenRequest(); + + } else { + throw new OAuth20InvalidRequestException(); + } + + res.setAction(action); + res.setModule(OAuth20Protocol.NAME); + + res.populateParameters(request); + return res; + } +} diff --git a/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20Protocol.java b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20Protocol.java new file mode 100644 index 000000000..56d86df72 --- /dev/null +++ b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20Protocol.java @@ -0,0 +1,215 @@ +package at.gv.egovernment.moa.id.protocols.oauth20.protocol; + +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.StringUtils; + +import at.gv.egovernment.moa.id.auth.exception.MOAIDException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.id.moduls.IAction; +import at.gv.egovernment.moa.id.moduls.IModulInfo; +import at.gv.egovernment.moa.id.moduls.IRequest; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Util; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20Exception; +import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; +import at.gv.egovernment.moa.id.util.ErrorResponseUtils; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; + +import com.google.gson.JsonObject; + +import java.util.Arrays; + +public class OAuth20Protocol implements IModulInfo { + + public static final String NAME = OAuth20Protocol.class.getName(); + public static final String PATH = "id_oauth20"; + + public static final String AUTH_ACTION = "AUTH"; + public static final String TOKEN_ACTION = "TOKEN"; + + public static final List<String> DEFAULTREQUESTEDATTRFORINTERFEDERATION = Arrays.asList( + new String[] { + PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, + PVPConstants.BPK_NAME + }); + + private static HashMap<String, IAction> actions = new HashMap<String, IAction>(); + + static { + actions.put(AUTH_ACTION, new OAuth20AuthAction()); + actions.put(TOKEN_ACTION, new OAuth20TokenAction()); + } + + public String getName() { + return NAME; + } + + public String getPath() { + return PATH; + } + + public IAction getAction(String action) { + return actions.get(action); + } + + /* + * (non-Javadoc) + * @see + * at.gv.egovernment.moa.id.moduls.IModulInfo#preProcess(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse, java.lang.String) + */ + public IRequest preProcess(HttpServletRequest request, HttpServletResponse resp, String action, + String sessionId, String transactionId) throws MOAIDException { + // validation is done inside creation + OAuth20BaseRequest res = OAuth20BaseRequest.newInstance(action, request, sessionId, transactionId); + Logger.debug("Created: " + res); + return res; + } + + /* + * (non-Javadoc) + * @see + * at.gv.egovernment.moa.id.moduls.IModulInfo#canHandleRequest(javax.servlet.http.HttpServletRequest + * , javax.servlet.http.HttpServletResponse) + */ + public IAction canHandleRequest(HttpServletRequest request, HttpServletResponse response) { + if (!StringUtils.isEmpty(request.getParameter("action"))) { + if (request.getParameter("action").equals(AUTH_ACTION)) { + return getAction(AUTH_ACTION); + } else if (request.getParameter("action").equals(TOKEN_ACTION)) { + return getAction(TOKEN_ACTION); + } + } + + return null;// getAction(AUTH_ACTION); + } + + /* + * (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IModulInfo#generateErrorMessage(java.lang.Throwable, + * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, + * at.gv.egovernment.moa.id.moduls.IRequest) + */ + public boolean generateErrorMessage(Throwable e, HttpServletRequest request, HttpServletResponse response, IRequest protocolRequest) + throws Throwable { + + // get error code and description + String errorCode; + String errorDescription; + String errorUri = AuthConfigurationProviderFactory.getInstance().getPublicURLPrefix() + +"/" + OAuth20Constants.ERRORPAGE; + String moaError = null; + + ErrorResponseUtils errorUtils = ErrorResponseUtils.getInstance(); + + if (e instanceof OAuth20Exception) { + errorCode = ((OAuth20Exception) e).getErrorCode(); + errorDescription = URLEncoder.encode(((OAuth20Exception) e).getMessageId() + ": " + e.getMessage(), "UTF-8"); + moaError = errorUtils.mapInternalErrorToExternalError(((OAuth20Exception) e).getMessageId()); + + } else { + errorCode = OAuth20Constants.ERROR_SERVER_ERROR; + errorDescription = URLEncoder.encode(e.getMessage(), "UTF-8"); + moaError = errorUtils.getResponseErrorCode(e); + } + + String paramRedirect = null; + String state = null; + boolean isAuthRequest = false; + if (protocolRequest != null) { + if (protocolRequest instanceof OAuth20AuthRequest) { + isAuthRequest = true; + + paramRedirect = ((OAuth20AuthRequest) protocolRequest).getRedirectUri(); + state = ((OAuth20AuthRequest) protocolRequest).getState(); + } else { + isAuthRequest = false; + } + } else { + String action = request.getParameter("action"); + if (MiscUtil.isNotEmpty(action)) { + if (action.equals(AUTH_ACTION)) { + + paramRedirect = request.getParameter(OAuth20Constants.PARAM_REDIRECT_URI); + state = request.getParameter(OAuth20Constants.PARAM_STATE); + isAuthRequest = true; + } + } else { + throw new MOAIDException("oauth20.01", new Object[] {}); + } + } + + // if (action.equals(AUTH_ACTION)) { + if (isAuthRequest) { + Logger.debug("Going to throw O OAuth20Exception for auth request"); + + StringBuilder url = new StringBuilder(); + + // check if given redirect url is ok + if (StringUtils.isNotEmpty(paramRedirect) && OAuth20Util.isUrl(paramRedirect)) { + url.append(paramRedirect); + + // otherwise throw an + } else { + throw new MOAIDException("oauth20.01", new Object[] {}); + } + + OAuth20Util.addParameterToURL(url, OAuth20Constants.PARAM_ERROR, errorCode); + OAuth20Util.addParameterToURL(url, OAuth20Constants.PARAM_ERROR_DESCRIPTION, errorDescription); + if (MiscUtil.isNotEmpty(moaError)) + OAuth20Util.addParameterToURL(url, OAuth20Constants.PARAM_ERROR_URI, errorUri + "#" + moaError); + OAuth20Util.addParameterToURL(url, OAuth20Constants.PARAM_STATE, state); + + response.setContentType("text/html"); + response.setStatus(HttpServletResponse.SC_FOUND); + response.addHeader("Location", url.toString()); + Logger.debug("REDIRECT TO: " + url.toString()); + return true; + + } else { + Logger.debug("Going to throw O OAuth20Exception for token request"); + + Map<String, Object> params = new HashMap<String, Object>(); + params.put(OAuth20Constants.PARAM_ERROR, errorCode); + params.put(OAuth20Constants.PARAM_ERROR_DESCRIPTION, errorDescription); + params.put(OAuth20Constants.PARAM_ERROR_URI, errorUri + "#" + moaError); + + // create response + JsonObject jsonObject = new JsonObject(); + OAuth20Util.addProperytiesToJsonObject(jsonObject, params); + String jsonResponse = jsonObject.toString(); + Logger.debug("JSON Response: " + jsonResponse); + + // write respone to http response + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + response.getOutputStream().print(jsonResponse); + response.getOutputStream().close(); + + return true; + } + + // return false; + + } + + /* + * (non-Javadoc) + * @see + * at.gv.egovernment.moa.id.moduls.IModulInfo#validate(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse, at.gv.egovernment.moa.id.moduls.IRequest) + */ + public boolean validate(HttpServletRequest request, HttpServletResponse response, IRequest pending) { + // we validate in the preProcess + return true; + } + +} diff --git a/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20TokenAction.java b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20TokenAction.java new file mode 100644 index 000000000..2238a25e1 --- /dev/null +++ b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20TokenAction.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +package at.gv.egovernment.moa.id.protocols.oauth20.protocol; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; +import at.gv.egovernment.moa.id.advancedlogging.MOAReversionLogger; +import at.gv.egovernment.moa.id.auth.exception.MOAIDException; +import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.data.SLOInformationInterface; +import at.gv.egovernment.moa.id.moduls.IAction; +import at.gv.egovernment.moa.id.moduls.IRequest; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20SessionObject; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Util; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20ServerErrorException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20UnauthorizedClientException; +import at.gv.egovernment.moa.id.storage.AssertionStorage; +import at.gv.egovernment.moa.logging.Logger; + +import com.google.gson.JsonObject; + +class OAuth20TokenAction implements IAction { + + public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, + IAuthData authData) throws MOAIDException { + + + OAuth20SessionObject auth20SessionObject = null; + try { + OAuth20TokenRequest oAuthRequest = (OAuth20TokenRequest) req; + + MOAReversionLogger.getInstance().logEvent(req, MOAIDEventConstants.AUTHPROTOCOL_OPENIDCONNECT_TOKENREQUEST); + + try { + Logger.debug("Loaded OAuth20SessionObject from session: " + oAuthRequest.getCode()); + + auth20SessionObject = + AssertionStorage.getInstance().get(oAuthRequest.getCode(), OAuth20SessionObject.class); + + } catch (MOADatabaseException e) { + throw new OAuth20UnauthorizedClientException(); + + } + + // do checking for different grant types and code + if (auth20SessionObject == null || !auth20SessionObject.getCode().equals(oAuthRequest.getCode())) { + throw new OAuth20UnauthorizedClientException(); + } else { + Logger.debug("Loaded of OAuth20SessionObject was successful"); + } + + // create response + JsonObject jsonObject = new JsonObject(); + OAuth20Util.addProperytiesToJsonObject(jsonObject, auth20SessionObject.getAuthDataSession()); + String jsonResponse = jsonObject.toString(); + Logger.debug("JSON Response: " + jsonResponse); + + // write respone to http response + httpResp.setContentType("application/json"); + httpResp.setStatus(HttpServletResponse.SC_OK); + httpResp.getOutputStream().print(jsonResponse); + httpResp.getOutputStream().close(); + + return null; + } + catch (Exception e) { + Logger.error(e.getMessage(), e); + throw new OAuth20ServerErrorException(); + } + + finally { + if (auth20SessionObject != null) { + // destroy session for clean up + + Logger.debug("Going to destroy session: " + auth20SessionObject.getCode()); + AssertionStorage.getInstance().remove(auth20SessionObject.getCode()); + + } + } + } + + /* + * (non-Javadoc) + * @see + * at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls + * .IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { + return false; + } + + /* + * (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() + */ + public String getDefaultActionName() { + return OAuth20Protocol.TOKEN_ACTION; + } + +} diff --git a/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20TokenRequest.java b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20TokenRequest.java new file mode 100644 index 000000000..6bebe5a6a --- /dev/null +++ b/id/server/modules/moa-id-module-openID/src/main/java/at/gv/egovernment/moa/id/protocols/oauth20/protocol/OAuth20TokenRequest.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + *******************************************************************************/ +package at.gv.egovernment.moa.id.protocols.oauth20.protocol; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.opensaml.saml2.core.Attribute; + +import at.gv.egovernment.moa.id.commons.config.MOAIDConfigurationConstants; +import at.gv.egovernment.moa.id.config.ConfigurationException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; +import at.gv.egovernment.moa.id.protocols.oauth20.OAuth20Constants; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20AccessDeniedException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20Exception; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20InvalidGrantException; +import at.gv.egovernment.moa.id.protocols.oauth20.exceptions.OAuth20WrongParameterException; +import at.gv.egovernment.moa.logging.Logger; + +class OAuth20TokenRequest extends OAuth20BaseRequest { + + private static final long serialVersionUID = 1L; + + private String code; + private String grantType; + private String clientID; + private String clientSecret; + + /** + * @return the code + */ + public String getCode() { + return code; + } + + /** + * @param code + * the code to set + */ + public void setCode(String code) { + this.code = code; + } + + /** + * @return the grantType + */ + public String getGrantType() { + return grantType; + } + + /** + * @param grantType + * the grantType to set + */ + public void setGrantType(String grantType) { + this.grantType = grantType; + } + + /** + * @return the clientID + */ + public String getClientID() { + return clientID; + } + + /** + * @param clientID + * the clientID to set + */ + public void setClientID(String clientID) { + this.clientID = clientID; + } + + /** + * @return the clientSecret + */ + public String getClientSecret() { + return clientSecret; + } + + /** + * @param clientSecret + * the clientSecret to set + */ + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + @Override + protected void populateSpecialParameters(HttpServletRequest request) throws OAuth20Exception { + this.setCode(this.getParam(request, OAuth20Constants.RESPONSE_CODE, true)); + this.setGrantType(this.getParam(request, OAuth20Constants.PARAM_GRANT_TYPE, true)); + this.setClientID(this.getParam(request, OAuth20Constants.PARAM_CLIENT_ID, true)); + this.setClientSecret(this.getParam(request, OAuth20Constants.PARAM_CLIENT_SECRET, true)); + + // check for grant type + if (!this.getGrantType().equals(OAuth20Constants.PARAM_GRANT_TYPE_VALUE_AUTHORIZATION_CODE)) { + throw new OAuth20InvalidGrantException(); + } + + // check if client id and secret are ok + try { + // OAOAUTH20 cannot be null at this point. check was done in base request + OAAuthParameter oaParam = AuthConfigurationProviderFactory.getInstance().getOnlineApplicationParameter(this.getOAURL()); + + if (!this.getClientID().equals(oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_CLIENTID))) { + throw new OAuth20AccessDeniedException(); + } + + if (!this.getClientSecret().equals(oaParam.getConfigurationValue(MOAIDConfigurationConstants.SERVICE_PROTOCOLS_OPENID_CLIENTSECRET))) { + throw new OAuth20AccessDeniedException(); + } + + this.setOnlineApplicationConfiguration(oaParam); + + } + catch (ConfigurationException e) { + throw new OAuth20WrongParameterException(OAuth20Constants.PARAM_CLIENT_ID); + } + + Logger.info("Dispatch OpenIDConnect TokenRequest: ClientID=" + this.clientID); + + //add valid parameters + this.allowedParameters.add(OAuth20Constants.PARAM_SCOPE); + this.allowedParameters.add(OAuth20Constants.PARAM_REDIRECT_URI); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.RequestImpl#getRequestedAttributes() + */ + @Override + public List<Attribute> getRequestedAttributes() { + return null; + } +} |