Search in sources :

Example 1 with DigestAuthorization

use of com.disney.http.auth.DigestAuthorization in project groovity by disney.

the class Ws method tag.

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object tag(Map attributes, Closure body) throws Exception {
    Object url = resolve(attributes, URL);
    if (url == null) {
        throw new RuntimeException("ws() requires 'url' attribute");
    }
    ScriptHelper context = getScriptHelper(body);
    Map variables = context.getBinding().getVariables();
    URI uri;
    URIBuilder builder;
    ArrayList<Header> headers;
    Function handlerFunction;
    Optional<UserPass> userPass;
    Optional<HttpSignatureSigner> signer;
    final AtomicReference openMessage = new AtomicReference<>();
    try {
        builder = new URIBuilder(url.toString());
        bind(context, Uri.CURRENT_URI_BUILDER, builder);
        headers = new ArrayList<Header>();
        bind(context, com.disney.groovity.tags.Header.CURRENT_LIST_FOR_HEADERS, headers);
        Credentials.acceptCredentials(variables);
        Signature.acceptSigner(variables);
        Object oldOut = get(context, OUT);
        StringWriter sw = new StringWriter();
        Object rval = null;
        bind(context, OUT, sw);
        try {
            rval = body.call();
            if (rval instanceof Writable) {
                ((Writable) rval).writeTo(sw);
            }
        } finally {
            bind(context, OUT, oldOut);
            userPass = Credentials.resolveCredentials(variables);
            signer = Signature.resolveSigner(variables);
        }
        String val = sw.toString().trim();
        if (val.length() > 0) {
            openMessage.set(val);
        } else if (rval != null) {
            openMessage.set(rval);
        }
        uri = builder.build();
        handlerFunction = (Function) get(body, Handler.HANDLER_BINDING);
    } catch (URISyntaxException e1) {
        throw new RuntimeException("Invalid URI " + url, e1);
    } finally {
        unbind(context, Uri.CURRENT_URI_BUILDER);
        unbind(context, com.disney.groovity.tags.Header.CURRENT_LIST_FOR_HEADERS);
        unbind(context, Handler.HANDLER_BINDING);
    }
    final Closure closer = resolve(attributes, CLOSE, Closure.class);
    final Closure errorHandler = resolve(attributes, ERROR, Closure.class);
    final Class messageFormat = resolve(attributes, MESSAGE, Class.class);
    final Integer timeout = resolve(attributes, TIMEOUT, Integer.class);
    final AtomicReference<WebSocket> socket = new AtomicReference<>();
    ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create();
    Session session;
    try {
        session = getContainer().connectToServer(new Endpoint() {

            @Override
            public void onOpen(Session session, EndpointConfig config) {
                try {
                    openCount.incrementAndGet();
                    if (timeout != null) {
                        session.setMaxIdleTimeout(timeout * 1000);
                    }
                    WebSocket ws = new WebSocket(session);
                    socket.set(ws);
                    ws.setName(uri.toString());
                    if (handlerFunction != null) {
                        ws.setMessageHandler(arg -> {
                            synchronized (handlerFunction) {
                                handlerFunction.apply(arg);
                            }
                        }, messageFormat);
                    }
                    if (openMessage.get() != null) {
                        ws.call(openMessage.get());
                    }
                } catch (Exception e) {
                    log.log(Level.SEVERE, "Error opening web socket session " + uri, e);
                }
            }

            @Override
            public void onClose(Session session, CloseReason reason) {
                try {
                    closeCount.incrementAndGet();
                    openSessions.remove(session);
                    if (closer != null) {
                        if (closer.getMaximumNumberOfParameters() > 0) {
                            closer.call(reason);
                        } else {
                            closer.call();
                        }
                    }
                } catch (Exception e) {
                    log.log(Level.SEVERE, "Error closing web socket session " + uri, e);
                }
            }

            @Override
            public void onError(Session session, Throwable th) {
                try {
                    errorCount.incrementAndGet();
                    if (errorHandler == null) {
                        throw th;
                    }
                    errorHandler.call(th);
                } catch (Throwable e) {
                    Level logLevel = Level.WARNING;
                    if (th != e) {
                        log.log(logLevel, "Error handling error for web socket session " + uri, e);
                    } else if (th instanceof IOException) {
                        logLevel = Level.FINE;
                    }
                    log.log(logLevel, "WebSocket client error: " + uri, th);
                }
            }
        }, configBuilder.configurator(new ClientEndpointConfig.Configurator() {

            public void beforeRequest(Map<String, List<String>> reqHeaders) {
                // copy programmatic headers
                for (Header header : headers) {
                    List<String> hl = reqHeaders.get(header.getName());
                    if (hl == null) {
                        hl = new ArrayList<>();
                        reqHeaders.put(header.getName(), hl);
                    }
                    hl.add(header.getValue());
                }
                Map<String, Map<String, String>> allChallenges = null;
                if (userPass.isPresent() || signer.isPresent()) {
                    allChallenges = getChallenges(uri, reqHeaders);
                }
                if (userPass.isPresent()) {
                    UserPass user = userPass.get();
                    if (allChallenges != null) {
                        List<String> auths = reqHeaders.get(AUTHORIZATION_HEADER);
                        if (auths == null) {
                            auths = new ArrayList<>();
                            reqHeaders.put(AUTHORIZATION_HEADER, auths);
                        }
                        if (allChallenges.containsKey("basic")) {
                            StringBuilder authBuilder = new StringBuilder(user.getUser());
                            authBuilder.append(":");
                            char[] pass = user.getPass();
                            for (char c : pass) {
                                authBuilder.append(c);
                            }
                            try {
                                auths.add("Basic " + printBase64Binary(authBuilder.toString().getBytes("UTF-8")));
                            } catch (UnsupportedEncodingException e) {
                                log.severe(e.getMessage());
                            }
                        }
                        if (allChallenges.containsKey("digest")) {
                            final String digestUri = uri.getPath() + ((uri.getRawQuery() != null) ? "?" + uri.getRawQuery() : "");
                            Map<String, String> digestChallenge = allChallenges.get("digest");
                            if (log.isLoggable(Level.FINE)) {
                                log.fine("Generating digest auth for " + digestChallenge.toString());
                            }
                            DigestAuthorization digestAuth = new DigestAuthorization();
                            digestAuth.setUsername(user.getUser());
                            digestAuth.setQop("auth");
                            digestAuth.setCnonce(String.valueOf(ThreadLocalRandom.current().nextLong(10000000, 999999999999l)));
                            digestAuth.setNonceCount("000001");
                            digestAuth.setUri(digestUri);
                            for (Entry<String, String> entry : digestChallenge.entrySet()) {
                                String k = entry.getKey();
                                String v = entry.getValue();
                                if ("nonce".equalsIgnoreCase(k)) {
                                    digestAuth.setNonce(v);
                                } else if ("realm".equalsIgnoreCase(k)) {
                                    digestAuth.setRealm(v);
                                } else if ("opaque".equalsIgnoreCase(k)) {
                                    digestAuth.setOpaque(v);
                                }
                            }
                            String signingString;
                            try {
                                signingString = digestAuth.generateSigningString(user.getUser(), new String(user.getPass()), new AuthorizationRequest() {

                                    @Override
                                    public String getURI() {
                                        return digestUri;
                                    }

                                    @Override
                                    public String getMethod() {
                                        return "GET";
                                    }

                                    @Override
                                    public List<String> getHeaders(String name) {
                                        return reqHeaders.get(name);
                                    }
                                });
                                MessageDigest md5 = MessageDigest.getInstance("MD5");
                                digestAuth.setDigest(md5.digest(signingString.toString().getBytes()));
                                if (log.isLoggable(Level.FINE)) {
                                    log.fine("Generated digest auth " + digestAuth.toString());
                                }
                                auths.add(digestAuth.toString());
                            } catch (NoSuchAlgorithmException e) {
                                log.severe("Missing MD5 " + e.getMessage());
                            }
                        }
                    }
                }
                if (signer.isPresent()) {
                    if (allChallenges.containsKey("signature")) {
                        HttpSignatureSigner sig = signer.get();
                        HttpGet signReq = createRequest(uri, reqHeaders);
                        List<Header> beforeHeaders = Arrays.asList(signReq.getAllHeaders());
                        try {
                            sig.process(signReq, null);
                        } catch (HttpException | IOException e) {
                            log.log(Level.SEVERE, "Error processing http signature", e);
                        }
                        Header[] afterHeaders = signReq.getAllHeaders();
                        for (Header h : afterHeaders) {
                            if (!beforeHeaders.contains(h)) {
                                List<String> hl = reqHeaders.get(h.getName());
                                if (hl == null) {
                                    hl = new ArrayList<>();
                                    reqHeaders.put(h.getName(), hl);
                                }
                                hl.add(h.getValue());
                                if (log.isLoggable(Level.FINE)) {
                                    log.fine("Copied HTTP signature header " + h);
                                }
                            }
                        }
                    }
                }
            }
        }).build(), uri);
    } catch (Exception e) {
        errorCount.incrementAndGet();
        throw e;
    }
    openSessions.add(session);
    String var = resolve(attributes, VAR, String.class);
    if (var != null) {
        context.getBinding().setVariable(var, socket.get());
    }
    return socket.get();
}
Also used : AuthorizationRequest(com.disney.http.auth.AuthorizationRequest) Writable(groovy.lang.Writable) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) Endpoint(javax.websocket.Endpoint) CloseReason(javax.websocket.CloseReason) MessageDigest(java.security.MessageDigest) UnsupportedEncodingException(java.io.UnsupportedEncodingException) URIBuilder(org.apache.http.client.utils.URIBuilder) Header(org.apache.http.Header) Level(java.util.logging.Level) ScriptHelper(com.disney.groovity.util.ScriptHelper) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) LinkedHashMap(java.util.LinkedHashMap) Closure(groovy.lang.Closure) HttpGet(org.apache.http.client.methods.HttpGet) URISyntaxException(java.net.URISyntaxException) URI(java.net.URI) Function(java.util.function.Function) StringWriter(java.io.StringWriter) HttpSignatureSigner(com.disney.http.auth.client.signer.HttpSignatureSigner) HttpException(org.apache.http.HttpException) ClientEndpointConfig(javax.websocket.ClientEndpointConfig) DigestAuthorization(com.disney.http.auth.DigestAuthorization) UserPass(com.disney.groovity.tags.Credentials.UserPass) AtomicReference(java.util.concurrent.atomic.AtomicReference) IOException(java.io.IOException) URISyntaxException(java.net.URISyntaxException) HttpException(org.apache.http.HttpException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException) WebSocket(com.disney.groovity.util.WebSocket) ClientEndpointConfig(javax.websocket.ClientEndpointConfig) EndpointConfig(javax.websocket.EndpointConfig) Session(javax.websocket.Session)

Example 2 with DigestAuthorization

use of com.disney.http.auth.DigestAuthorization in project groovity by disney.

the class TestDigestAuth method testDigest.

@Test
public void testDigest() throws Exception {
    DigestVerifierImpl verifier = new DigestVerifierImpl();
    Map<String, String> pmap = new HashMap<String, String>();
    List<String> accessList = new ArrayList<String>();
    ACLAccessControllerImpl acl = new ACLAccessControllerImpl();
    acl.setAcl(accessList);
    pmap.put("mykey", "mypass");
    PasswordDigester pc = new MapPasswordDigester(pmap);
    verifier.setPasswordDigesters(Arrays.asList(pc));
    verifier.setAccessControllers(Arrays.asList((AccessController) acl));
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setRequestURI("/");
    ServerAuthorizationRequest areq = new ServletAuthorizationRequest(request);
    VerifierResult result = verifier.verify(areq);
    Assert.assertEquals(ERROR_MISSING_CREDENTIALS, result.getMessage());
    String challenge = result.getChallenge();
    Pattern noncePattern = Pattern.compile("nonce=\"([^\"]+)\"");
    Matcher matcher = noncePattern.matcher(challenge);
    if (!matcher.find()) {
        throw new Exception("No nonce found in challenge");
    }
    String nonce = matcher.group(1);
    Pattern opaquePattern = Pattern.compile("opaque=\"([^\"]+)\"");
    matcher = opaquePattern.matcher(challenge);
    if (!matcher.find()) {
        throw new Exception("No opaque found in challenge");
    }
    String opaque = matcher.group(1);
    DigestAuthorization ad = new DigestAuthorization();
    ad.setNonce(nonce);
    ad.setCnonce("ClientNonce");
    ad.setNonceCount("000001");
    ad.setOpaque(opaque);
    ad.setQop("auth");
    ad.setUri("/");
    ad.setUsername("mykey");
    ad.setDigest(new byte[0]);
    ad.setRealm(verifier.getRealm());
    request.addHeader("Authorization", ad.toString());
    result = verifier.verify(areq);
    Assert.assertEquals(ERROR_UNKNOWN_CREDENTIALS, result.getMessage());
    // now fix the digest
    /*
		StringBuilder signingString = new StringBuilder();
		signingString.append(digest("mykey",verifier.getRealm(),"mypass"));
		signingString.append(":").append(nonce).append(":").append(ad.getNonceCount()).append(":").append(ad.getCnonce()).append(":auth:");
		signingString.append(digest("GET",ad.getUri()));
		*/
    request = new MockHttpServletRequest();
    areq = new ServletAuthorizationRequest(request);
    request.setMethod("GET");
    request.setRequestURI("/");
    String signingString = ad.generateSigningString("mykey", "mypass", new ServletAuthorizationRequest(request));
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    ad.setDigest(md5.digest(signingString.toString().getBytes()));
    request.addHeader("Authorization", ad.toString());
    result = verifier.verify(areq);
    Assert.assertTrue("Expected successful authentication", result.isAuthenticated());
    Assert.assertFalse("Expected failed authorization", result.isAuthorized());
    accessList.add("mykey");
    result = verifier.verify(areq);
    Assert.assertTrue("Expected successful authentication", result.isAuthenticated());
    Assert.assertTrue("Expected successful authorization", result.isAuthorized());
}
Also used : DigestAuthorization(com.disney.http.auth.DigestAuthorization) Pattern(java.util.regex.Pattern) HashMap(java.util.HashMap) MapPasswordDigester(com.disney.http.auth.server.digest.MapPasswordDigester) PasswordDigester(com.disney.http.auth.server.digest.PasswordDigester) Matcher(java.util.regex.Matcher) MockHttpServletRequest(org.springframework.mock.web.MockHttpServletRequest) ArrayList(java.util.ArrayList) ServletAuthorizationRequest(com.disney.http.auth.server.ServletAuthorizationRequest) MapPasswordDigester(com.disney.http.auth.server.digest.MapPasswordDigester) AccessController(com.disney.http.auth.server.AccessController) VerifierResult(com.disney.http.auth.server.VerifierResult) MessageDigest(java.security.MessageDigest) ACLAccessControllerImpl(com.disney.http.auth.server.ACLAccessControllerImpl) DigestVerifierImpl(com.disney.http.auth.server.digest.DigestVerifierImpl) ServerAuthorizationRequest(com.disney.http.auth.server.ServerAuthorizationRequest) Test(org.junit.Test)

Example 3 with DigestAuthorization

use of com.disney.http.auth.DigestAuthorization in project groovity by disney.

the class DigestVerifierImpl method doVerifyInternal.

@Override
protected VerifierResult doVerifyInternal(ServerAuthorizationRequest request) throws NoSuchAlgorithmException, IOException {
    VerifierResult result = new VerifierResult();
    List<String> headers = request.getHeaders(AUTHORIZATION_HEADER);
    String authHeader = null;
    for (String header : headers) {
        if (header.startsWith(DIGEST)) {
            authHeader = header;
            break;
        }
    }
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    if (authHeader == null) {
        challenge(request, md5, result, ERROR_MISSING_CREDENTIALS, false);
        return result;
    }
    DigestAuthorization authd;
    try {
        authd = new DigestAuthorization(authHeader);
    } catch (Exception e) {
        challenge(request, md5, result, e.getMessage(), false);
        return result;
    }
    // validate URI
    if (!request.getURI().equals(authd.getUri())) {
        challenge(request, md5, result, ERROR_INCORRECT_URI, false);
        return result;
    }
    String nonce = authd.getNonce();
    byte[] nonceBytes = DatatypeConverter.parseBase64Binary(nonce);
    // validate timestamp
    long timestamp = toLong(nonceBytes);
    // validate nonce
    if (!nonce.equals(makeNonce(md5, timestamp))) {
        challenge(request, md5, result, ERROR_INVALID_NONCE, false);
        return result;
    }
    // validate digest
    byte[] ha2 = DatatypeConverter.printHexBinary(md5.digest((request.getMethod() + ":" + authd.getUri()).getBytes("UTF-8"))).toLowerCase().getBytes();
    for (int i = 0; i < passwordDigesters.size(); i++) {
        PasswordDigester digester = passwordDigesters.get(i);
        byte[] ha1 = digester.digest(authd.getUsername(), getRealm());
        if (ha1 != null) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(200);
            baos.write(DatatypeConverter.printHexBinary(ha1).toLowerCase().getBytes());
            baos.write((byte) ':');
            baos.write(nonce.getBytes());
            baos.write((byte) ':');
            if ("auth".equals(authd.getQop())) {
                baos.write(authd.getNonceCount().getBytes());
                baos.write((byte) ':');
                baos.write(authd.getCnonce().getBytes("UTF-8"));
                baos.write((byte) ':');
                baos.write(authd.getQop().getBytes());
                baos.write((byte) ':');
            }
            byte[] rd = baos.toByteArray();
            md5.reset();
            md5.update(rd);
            md5.update(ha2);
            byte[] digestVal = md5.digest();
            if (Arrays.equals(digestVal, authd.getDigest())) {
                if ("auth".equals(authd.getQop())) {
                    md5.reset();
                    md5.update(rd);
                    md5.update(DatatypeConverter.printHexBinary(md5.digest((":" + authd.getUri()).getBytes("UTF-8"))).toLowerCase().getBytes());
                    byte[] rspAuth = md5.digest();
                    result.setAuthenticationInfo("qop=\"" + authd.getQop() + "\",cnonce=\"" + authd.getCnonce() + "\",nc=" + authd.getNonceCount() + ",rspauth=\"" + DatatypeConverter.printHexBinary(rspAuth).toLowerCase() + "\"");
                }
                if ((System.currentTimeMillis() - timestamp) > maxNonceAge) {
                    challenge(request, md5, result, ERROR_STALE_NONCE, true);
                } else {
                    result.setAuthenticated(true);
                    result.setPrincipal(new AuthenticatedPrincipal(authd.getUsername()));
                }
                return result;
            }
        }
    }
    challenge(request, md5, result, ERROR_UNKNOWN_CREDENTIALS, false);
    return result;
}
Also used : DigestAuthorization(com.disney.http.auth.DigestAuthorization) VerifierResult(com.disney.http.auth.server.VerifierResult) ByteArrayOutputStream(java.io.ByteArrayOutputStream) MessageDigest(java.security.MessageDigest) IOException(java.io.IOException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) AuthenticatedPrincipal(com.disney.http.auth.server.AuthenticatedPrincipal)

Aggregations

DigestAuthorization (com.disney.http.auth.DigestAuthorization)3 MessageDigest (java.security.MessageDigest)3 VerifierResult (com.disney.http.auth.server.VerifierResult)2 IOException (java.io.IOException)2 UnsupportedEncodingException (java.io.UnsupportedEncodingException)2 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)2 UserPass (com.disney.groovity.tags.Credentials.UserPass)1 ScriptHelper (com.disney.groovity.util.ScriptHelper)1 WebSocket (com.disney.groovity.util.WebSocket)1 AuthorizationRequest (com.disney.http.auth.AuthorizationRequest)1 HttpSignatureSigner (com.disney.http.auth.client.signer.HttpSignatureSigner)1 ACLAccessControllerImpl (com.disney.http.auth.server.ACLAccessControllerImpl)1 AccessController (com.disney.http.auth.server.AccessController)1 AuthenticatedPrincipal (com.disney.http.auth.server.AuthenticatedPrincipal)1 ServerAuthorizationRequest (com.disney.http.auth.server.ServerAuthorizationRequest)1 ServletAuthorizationRequest (com.disney.http.auth.server.ServletAuthorizationRequest)1 DigestVerifierImpl (com.disney.http.auth.server.digest.DigestVerifierImpl)1 MapPasswordDigester (com.disney.http.auth.server.digest.MapPasswordDigester)1 PasswordDigester (com.disney.http.auth.server.digest.PasswordDigester)1 Closure (groovy.lang.Closure)1