GraniteProvider.java 14 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  org.apache.commons.codec.binary.Base64
 *  org.apache.commons.lang3.StringUtils
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.ReferenceCardinality
 *  org.apache.felix.scr.annotations.ReferencePolicy
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.jackrabbit.api.security.user.Authorizable
 *  org.apache.jackrabbit.api.security.user.User
 *  org.apache.sling.api.SlingHttpServletRequest
 *  org.apache.sling.commons.json.JSONException
 *  org.apache.sling.commons.json.JSONObject
 *  org.apache.sling.commons.osgi.PropertiesUtil
 *  org.apache.sling.commons.osgi.ServiceUtil
 *  org.osgi.service.component.ComponentContext
 *  org.scribe.builder.api.Api
 *  org.scribe.model.OAuthRequest
 *  org.scribe.model.Response
 *  org.scribe.model.Verb
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.auth.oauth.impl;

import com.adobe.granite.auth.oauth.Provider;
import com.adobe.granite.auth.oauth.ProviderExtension;
import com.adobe.granite.auth.oauth.ProviderType;
import com.adobe.granite.auth.oauth.impl.GraniteApi;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.commons.osgi.ServiceUtil;
import org.osgi.service.component.ComponentContext;
import org.scribe.builder.api.Api;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Verb;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * This class specifies class file version 49.0 but uses Java 6 signatures.  Assumed Java 6.
 */
@Service(value={Provider.class})
@Component(metatype=1, label="Adobe Granite OAuth Provider", description="Default Granite OAuth Provider")
@Reference(name="providerExtension", referenceInterface=ProviderExtension.class, bind="bindProviderExtension", unbind="unbindProviderExtension", cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC)
public class GraniteProvider
implements Provider {
    private final Logger log = LoggerFactory.getLogger(GraniteProvider.class);
    private static final String DEFAULT_OAUTH_PROVIDER_ID = "granite";
    @Property(value={"granite"}, label="Provider ID", description="Assign a unique Provider ID")
    private static final String PROP_OAUTH_PROVIDER_ID = "oauth.provider.id";
    private static final String DEFAULT_GRANITE_AUTHORIZATION_URL = "";
    @Property(value={""}, label="Authorization Endpoint", description="The URL of the Granite Authorization Endpoint")
    private static final String PROP_GRANITE_AUTHORIZATION_URL = "oauth.provider.granite.authorization.url";
    private static final String DEFAULT_GRANITE_TOKEN_URL = "";
    @Property(value={""}, label="Token Endpoint", description="The URL of the Granite Token Endpoint")
    private static final String PROP_GRANITE_TOKEN_URL = "oauth.provider.granite.token.url";
    private static final String DEFAULT_GRANITE_PROFILE_URL = "";
    @Property(value={""}, label="Profile Endpoint", description="The URL of the IMS Profile Endpoint")
    private static final String PROP_GRANITE_PROFILE_URL = "oauth.provider.granite.profile.url";
    @Property(label="Extended Details URLs", description="The list of URLs used to fetch additional data")
    private static final String PROP_EXTENDED_DETAILS_URLS = "oauth.provider.granite.extended.details.urls";
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private SortedMap<Comparable<Object>, ProviderExtension> registeredProviderExtensionHandlers = new TreeMap(Collections.reverseOrder());
    private ProviderType type = ProviderType.OAUTH2;
    private GraniteApi graniteApi;
    private String authorizationUrl;
    private String tokenUrl;
    private String detailsURL;
    private String[] extendedDetailsURLs;
    private String id;
    private String name;

    @Activate
    protected void activate(ComponentContext componentContext) throws Exception {
        Dictionary properties = componentContext.getProperties();
        this.graniteApi = new GraniteApi();
        this.name = PropertiesUtil.toString(properties.get("service.description"), (String)"");
        this.id = PropertiesUtil.toString(properties.get("oauth.provider.id"), (String)"granite");
        this.authorizationUrl = PropertiesUtil.toString(properties.get("oauth.provider.granite.authorization.url"), (String)"");
        this.tokenUrl = PropertiesUtil.toString(properties.get("oauth.provider.granite.token.url"), (String)"");
        this.detailsURL = PropertiesUtil.toString(properties.get("oauth.provider.granite.profile.url"), (String)"");
        this.extendedDetailsURLs = PropertiesUtil.toStringArray(properties.get("oauth.provider.granite.extended.details.urls"), (String[])new String[0]);
        this.graniteApi.setAuthorizationUrl(this.authorizationUrl);
        this.graniteApi.setAccessTokenEndpoint(this.tokenUrl);
    }

    @Override
    public ProviderType getType() {
        return this.type;
    }

    @Override
    public Api getApi() {
        return this.graniteApi;
    }

    @Override
    public String getDetailsURL() {
        return this.detailsURL;
    }

    @Override
    public String[] getExtendedDetailsURLs(String scope) {
        return this.extendedDetailsURLs;
    }

    @Override
    public String[] getExtendedDetailsURLs(String scope, String userId, Map<String, Object> props) {
        throw new UnsupportedOperationException("This provider doesn't support getExtendedDetailsURLs(String scope, String userId,Map<String, Object> props) method");
    }

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String mapUserId(String userId, Map<String, Object> props) {
        ProviderExtension providerExtension = this.getProviderExtension();
        if (providerExtension != null) {
            String delegatedUserId = providerExtension.mapUserId(userId, props);
            return delegatedUserId != null ? delegatedUserId : this.getDefaultUserId(userId, props);
        }
        return this.getDefaultUserId(userId, props);
    }

    @Override
    public String getUserFolderPath(String userId, String clientId, Map<String, Object> props) {
        ProviderExtension providerExtension = this.getProviderExtension();
        if (providerExtension != null) {
            String userFolderPath = providerExtension.getUserFolderPath(userId, clientId, props);
            return userFolderPath != null ? userFolderPath : this.getDefaultUserFolderPath(userId, clientId, props);
        }
        return this.getDefaultUserFolderPath(userId, clientId, props);
    }

    @Override
    public Map<String, Object> mapProperties(String srcUrl, String clientId, Map<String, Object> existing, Map<String, String> newProperties) {
        HashMap<String, Object> mapped = new HashMap<String, Object>();
        mapped.putAll(existing);
        for (Map.Entry<String, String> prop : newProperties.entrySet()) {
            String property = prop.getKey();
            String value = prop.getValue();
            String mappedKey = this.mapProperty(property);
            Object mappedValue = this.mapValue(mappedKey, value);
            mapped.put(mappedKey, mappedValue);
        }
        return mapped;
    }

    @Override
    public String getAccessTokenPropertyPath(String clientId) {
        return "profile/app-" + clientId;
    }

    @Override
    public String getOAuthIdPropertyPath(String clientId) {
        return "oauth/oauthid-" + clientId;
    }

    @Override
    public User getCurrentUser(SlingHttpServletRequest request) {
        Authorizable authorizable = (Authorizable)request.adaptTo(Authorizable.class);
        if (authorizable != null && !authorizable.isGroup()) {
            return (User)authorizable;
        }
        return null;
    }

    @Override
    public void onUserCreate(User user) {
        ProviderExtension providerExtension = this.getProviderExtension();
        if (providerExtension != null) {
            providerExtension.onUserCreate(user);
        }
    }

    @Override
    public void onUserUpdate(User user) {
        ProviderExtension providerExtension = this.getProviderExtension();
        if (providerExtension != null) {
            providerExtension.onUserUpdate(user);
        }
    }

    @Override
    public OAuthRequest getProtectedDataRequest(String url) {
        return new OAuthRequest(Verb.GET, url);
    }

    @Override
    public Map<String, String> parseProfileDataResponse(Response response) throws IOException {
        String body = null;
        try {
            body = response.getBody();
            HashMap<String, String> newProps = new HashMap<String, String>();
            if (!StringUtils.isEmpty((CharSequence)body)) {
                JSONObject json = new JSONObject(body);
                Iterator keys = json.keys();
                while (keys.hasNext()) {
                    String key = (String)keys.next();
                    if ("path".equals(key) || key.contains("_xss")) continue;
                    if ("user".equals(key)) {
                        JSONObject jsonUser = json.optJSONObject(key);
                        newProps.put("authorizableId", jsonUser.optString("authorizableId"));
                        continue;
                    }
                    newProps.put(key, json.optString(key));
                }
            } else {
                this.log.debug("Unable to parse json response body: {}", (Object)body);
            }
            return newProps;
        }
        catch (JSONException je) {
            this.log.debug("problem parsing JSON; response body was: {}", (Object)body);
            throw new IOException(je.toString());
        }
        catch (Exception e) {
            this.log.error("Exception while parsing profile data");
            throw new IOException(e.toString());
        }
    }

    @Override
    public String getUserIdProperty() {
        return "authorizableId";
    }

    @Override
    public String getValidateTokenUrl(String clientId, String token) {
        throw new UnsupportedOperationException("This provider doesn't support the validation of a token");
    }

    @Override
    public boolean isValidToken(String responseBody, String clientId, String tokenType) {
        throw new UnsupportedOperationException("This provider doesn't support the validation of a token");
    }

    @Override
    public String getUserIdFromValidateTokenResponseBody(String responseBody) {
        throw new UnsupportedOperationException("This provider doesn't support the validation of a token");
    }

    @Override
    public String getErrorDescriptionFromValidateTokenResponseBody(String responseBody) {
        throw new UnsupportedOperationException("This provider doesn't support the validation of a token");
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void bindProviderExtension(ProviderExtension providerExtension, Map<String, Object> properties) {
        this.lock.writeLock().lock();
        try {
            this.registeredProviderExtensionHandlers.put(ServiceUtil.getComparableForServiceRanking(properties), providerExtension);
        }
        finally {
            this.lock.writeLock().unlock();
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    protected void unbindProviderExtension(ProviderExtension providerExtension, Map<String, Object> properties) {
        this.lock.writeLock().lock();
        try {
            this.registeredProviderExtensionHandlers.remove(ServiceUtil.getComparableForServiceRanking(properties));
        }
        finally {
            this.lock.writeLock().unlock();
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private ProviderExtension getProviderExtension() {
        this.lock.readLock().lock();
        try {
            for (ProviderExtension pe : this.registeredProviderExtensionHandlers.values()) {
                if (pe.getId() == null || !pe.getId().equals(this.getId())) continue;
                ProviderExtension providerExtension = pe;
                return providerExtension;
            }
        }
        finally {
            this.lock.readLock().unlock();
        }
        return null;
    }

    private String getDefaultUserId(String userId, Map<String, Object> props) {
        return this.getId() + "-" + userId;
    }

    private String getDefaultUserFolderPath(String userId, String clientId, Map<String, Object> props) {
        String spread = new String(Base64.encodeBase64((byte[])userId.getBytes())).substring(0, 4);
        return this.getId() + "/" + spread;
    }

    private String mapProperty(String property) {
        return "profile/" + property;
    }

    private Object mapValue(String key, String value) {
        return value;
    }
}