org.jasig.ssp.service.security.oauth2.impl.OAuth2ClientServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.ssp.service.security.oauth2.impl.OAuth2ClientServiceImpl.java

Source

/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.jasig.ssp.service.security.oauth2.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.jasig.ssp.dao.AuditableCrudDao;
import org.jasig.ssp.dao.security.oauth2.OAuth2ClientDao;
import org.jasig.ssp.factory.OAuth2ClientTOFactory;
import org.jasig.ssp.model.ObjectStatus;
import org.jasig.ssp.model.security.PersistentGrantedAuthority;
import org.jasig.ssp.model.security.oauth2.OAuth2Client;
import org.jasig.ssp.service.AbstractAuditableCrudService;
import org.jasig.ssp.service.ObjectNotFoundException;
import org.jasig.ssp.service.security.oauth2.OAuth2ClientService;
import org.jasig.ssp.transferobject.OAuth2ClientTO;
import org.jasig.ssp.web.api.validation.ValidationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.BaseClientDetails;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("oauth2ClientDetailsService")
@Transactional
public class OAuth2ClientServiceImpl extends AbstractAuditableCrudService<OAuth2Client>
        implements OAuth2ClientService, ClientDetailsService {

    @Autowired
    private OAuth2ClientTOFactory factory;

    @Autowired
    private OAuth2ClientDao oAuth2ClientDao;

    @Autowired
    private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();

    @Autowired
    private ConsumerTokenServices consumerTokenService;

    @Autowired
    private ResourceServerTokenServices resourceServerTokenService;

    @Override
    protected AuditableCrudDao<OAuth2Client> getDao() {
        return oAuth2ClientDao;
    }

    /**
     * @param passwordEncoder the password encoder to set
     */
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * Overridden to throw an {@link UnsupportedOperationException}. Use
     * {@link #create(org.jasig.ssp.transferobject.OAuth2ClientTO)} so the
     * service knows exactly what you're trying to do.
     *
     * @param client
     * @return
     * @throws ObjectNotFoundException
     * @throws ValidationException
     */
    @Override
    public OAuth2Client create(final OAuth2Client client) throws ObjectNotFoundException, ValidationException {
        throw new UnsupportedOperationException("Use create(OAuth2ClientTO)");
    }

    /**
     * Overridden to throw an {@link UnsupportedOperationException}. Use
     * {@link #save(org.jasig.ssp.transferobject.OAuth2ClientTO)} so the
     * service knows exactly what you're trying to do.
     *
     * @param client
     * @return
     * @throws ObjectNotFoundException
     * @throws ValidationException
     */
    @Override
    public OAuth2Client save(OAuth2Client client) throws ObjectNotFoundException, ValidationException {
        throw new UnsupportedOperationException("Use save(OAuth2ClientTO)");
    }

    @Override
    public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
        OAuth2Client client = ((OAuth2ClientDao) getDao()).findByClientId(clientId);
        return client == null ? null : asClientDetails(client);
    }

    // Don't want any Hib references hanging around, so we create a whole new
    // object rather than pass the Hib entity into the guts of SpringSecurity
    private ClientDetails asClientDetails(OAuth2Client client) {
        // We happen to know all these setters will take defensive copies of
        // Hib collections, so we don't really have anything to worry about
        // w/r/t stale sessions. Also note that we don't support the full
        // breadth of ClientDetail features right now, so the list of property
        // setters here is intentionally short.
        BaseClientDetails clientDetails = new BaseClientDetails();
        clientDetails.setClientId(client.getClientId());
        clientDetails.setClientSecret(client.getSecret());
        clientDetails.setAccessTokenValiditySeconds(client.getAccessTokenValiditySeconds());
        clientDetails.setAuthorities(client.getAuthorities());
        clientDetails.setAuthorizedGrantTypes(client.getAuthorizedGrantTypes());
        return clientDetails;
    }

    @Override
    public OAuth2Client create(OAuth2ClientTO obj) throws ObjectNotFoundException, ValidationException {
        final OAuth2Client model = factory.from(obj);
        beforeWrite(model, obj);
        return super.create(model);
    }

    @Override
    public OAuth2Client save(OAuth2ClientTO obj) throws ObjectNotFoundException {
        OAuth2Client existingModel = oAuth2ClientDao.get(obj.getId());
        OAuth2Client newModel = null;
        boolean invalidateClientOnlyTokens = false;
        String previousClientId = null;
        if (existingModel != null) {
            previousClientId = existingModel.getClientId();
            if (obj.isSecretChange()) {
                invalidateClientOnlyTokens = true;
            } else {
                final List<PersistentGrantedAuthority> existingAuthorities = new ArrayList<PersistentGrantedAuthority>(
                        existingModel.getAuthorities());
                newModel = factory.from(obj);
                invalidateClientOnlyTokens = !(areMatching(existingAuthorities, newModel.getAuthorities()));
            }
        }

        if (newModel == null) {
            newModel = factory.from(obj);
        }

        beforeWrite(newModel, obj);
        newModel = getDao().save(newModel);

        // Save has side-effects on objectStatus so wait until here to check it
        boolean invalidateAllTokens = false;
        invalidateAllTokens = existingModel != null && (!(previousClientId.equals(newModel.getClientId()))
                || (newModel.getObjectStatus() != null && newModel.getObjectStatus() != ObjectStatus.ACTIVE));

        if (invalidateAllTokens) {
            invalidateAllTokensByClientId(previousClientId);
        } else if (invalidateClientOnlyTokens) {
            invalidateClientOnlyTokensByClientId(previousClientId);
            ;
        }

        return newModel;
    }

    private void invalidateAllTokensByClientId(String clientId) {
        final Collection<OAuth2AccessToken> tokensByClientId = consumerTokenService
                .findTokensByClientId(StringUtils.lowerCase(clientId));
        for (OAuth2AccessToken token : tokensByClientId) {
            consumerTokenService.revokeToken(token.getValue());
        }
    }

    private void invalidateClientOnlyTokensByClientId(String clientId) {
        final Collection<OAuth2AccessToken> tokensByClientId = consumerTokenService
                .findTokensByClientId(StringUtils.lowerCase(clientId));
        for (OAuth2AccessToken token : tokensByClientId) {
            final OAuth2Authentication authN = resourceServerTokenService.loadAuthentication(token.getValue());
            if (authN.isClientOnly()) {
                consumerTokenService.revokeToken(token.getValue());
            }
        }
    }

    private void beforeWrite(OAuth2Client client, OAuth2ClientTO from) {
        if (from.isSecretChange()) {
            encodeSecret(client);
        }
        ensureSchoolId(client);
    }

    private void encodeSecret(OAuth2Client client) {
        client.setSecret(StringUtils.trimToNull(client.getSecret()));
        if (client.getSecret() != null) {
            client.setSecret(passwordEncoder.encode(client.getSecret()));
        }
    }

    private void ensureSchoolId(OAuth2Client client) {
        if (StringUtils.isBlank(client.getSchoolId())) {
            client.setSchoolId(client.getClientId());
        }
    }

    private boolean areMatching(List<PersistentGrantedAuthority> auth1, List<PersistentGrantedAuthority> auth2) {
        if (auth1 == auth2) {
            return true;
        }
        if (auth1 == null || auth2 == null) {
            return false;
        }
        return auth1.equals(auth2);
    }
}