001/**
002 * Copyright (C) 2006-2020 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.server.service.httpurlconnection;
017
018import java.lang.reflect.InvocationTargetException;
019import java.lang.reflect.Method;
020import java.lang.reflect.Modifier;
021import java.net.Authenticator;
022import java.net.PasswordAuthentication;
023import java.net.URL;
024import java.util.stream.Stream;
025
026import javax.annotation.PostConstruct;
027import javax.annotation.PreDestroy;
028import javax.enterprise.context.ApplicationScoped;
029import javax.inject.Inject;
030
031@ApplicationScoped
032public class NetAuthenticatorWorkaround {
033
034    @Inject
035    private NetAuthenticatorController controller;
036
037    private volatile Authenticator original;
038
039    @PostConstruct
040    private void init() {
041        original = getAuthenticator();
042        Authenticator.setDefault(new ApplicationAuthenticator(original, controller));
043    }
044
045    public void lazyInit() {
046        // no-op
047    }
048
049    @PreDestroy
050    private void destroy() {
051        Authenticator.setDefault(original);
052    }
053
054    private Authenticator getAuthenticator() {
055        return Stream
056                .of(Authenticator.class.getDeclaredFields())
057                .filter(f -> Modifier.isStatic(f.getModifiers()) && Modifier.isPrivate(f.getModifiers())
058                        && f.getType() == Authenticator.class)
059                .findFirst()
060                .map(f -> {
061                    if (!f.isAccessible()) {
062                        f.setAccessible(true);
063                    }
064                    try {
065                        return Authenticator.class.cast(f.get(null));
066                    } catch (final IllegalAccessException e) {
067                        throw new IllegalStateException(e);
068                    }
069                })
070                .orElse(null);
071    }
072
073    private static class ApplicationAuthenticator extends Authenticator {
074
075        private final Authenticator delegate;
076
077        private final NetAuthenticatorController controller;
078
079        private final Method getRequestorType;
080
081        private final Method getPasswordAuthentication;
082
083        private final Method getRequestingURL;
084
085        private ApplicationAuthenticator(final Authenticator original, final NetAuthenticatorController controller) {
086            this.getPasswordAuthentication = findMethod("getPasswordAuthentication");
087            this.getRequestingURL = findMethod("getRequestingURL");
088            this.getRequestorType = findMethod("getRequestorType");
089            this.delegate = original;
090            this.controller = controller;
091        }
092
093        private Method findMethod(final String name) {
094            final Method declaredMethod;
095            try {
096                declaredMethod = Authenticator.class.getDeclaredMethod(name);
097            } catch (final NoSuchMethodException e) {
098                throw new IllegalStateException(e);
099            }
100            if (!declaredMethod.isAccessible()) {
101                declaredMethod.setAccessible(true);
102            }
103            return declaredMethod;
104        }
105
106        private boolean shouldSkip() {
107            return delegate == null || controller.isSkipped();
108        }
109
110        @Override
111        public PasswordAuthentication getPasswordAuthentication() {
112            try {
113                return shouldSkip() ? null
114                        : PasswordAuthentication.class.cast(getPasswordAuthentication.invoke(delegate));
115            } catch (final IllegalAccessException e) {
116                throw new IllegalStateException(e);
117            } catch (final InvocationTargetException e) {
118                throw new IllegalStateException(e.getTargetException());
119            }
120        }
121
122        @Override
123        public URL getRequestingURL() {
124            try {
125                return shouldSkip() ? null : URL.class.cast(getRequestingURL.invoke(delegate));
126            } catch (final IllegalAccessException e) {
127                throw new IllegalStateException(e);
128            } catch (final InvocationTargetException e) {
129                throw new IllegalStateException(e.getTargetException());
130            }
131        }
132
133        @Override
134        public RequestorType getRequestorType() {
135            try {
136                return shouldSkip() ? null : RequestorType.class.cast(getRequestorType.invoke(delegate));
137            } catch (final IllegalAccessException e) {
138                throw new IllegalStateException(e);
139            } catch (final InvocationTargetException e) {
140                throw new IllegalStateException(e.getTargetException());
141            }
142        }
143    }
144}