001/*******************************************************************************
002 * Copyright 2017 The MIT Internet Trust Consortium
003 *
004 * Portions copyright 2011-2013 The MITRE Corporation
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *******************************************************************************/
018/**
019 *
020 */
021package org.mitre.oauth2.service.impl;
022
023import java.util.Collection;
024import java.util.Date;
025
026import org.mitre.data.AbstractPageOperationTemplate;
027import org.mitre.oauth2.model.AuthenticationHolderEntity;
028import org.mitre.oauth2.model.AuthorizationCodeEntity;
029import org.mitre.oauth2.repository.AuthenticationHolderRepository;
030import org.mitre.oauth2.repository.AuthorizationCodeRepository;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033import org.springframework.beans.factory.annotation.Autowired;
034import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
035import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
036import org.springframework.security.oauth2.provider.OAuth2Authentication;
037import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
038import org.springframework.stereotype.Service;
039import org.springframework.transaction.annotation.Transactional;
040
041/**
042 * Database-backed, random-value authorization code service implementation.
043 *
044 * @author aanganes
045 *
046 */
047@Service("defaultOAuth2AuthorizationCodeService")
048public class DefaultOAuth2AuthorizationCodeService implements AuthorizationCodeServices {
049        // Logger for this class
050        private static final Logger logger = LoggerFactory.getLogger(DefaultOAuth2AuthorizationCodeService.class);
051
052        @Autowired
053        private AuthorizationCodeRepository repository;
054
055        @Autowired
056        private AuthenticationHolderRepository authenticationHolderRepository;
057
058        private int authCodeExpirationSeconds = 60 * 5; // expire in 5 minutes by default
059
060        private RandomValueStringGenerator generator = new RandomValueStringGenerator();
061
062        /**
063         * Generate a random authorization code and create an AuthorizationCodeEntity,
064         * which will be stored in the repository.
065         *
066         * @param authentication        the authentication of the current user, to be retrieved when the
067         *                                                      code is consumed
068         * @return                                      the authorization code
069         */
070        @Override
071        @Transactional(value="defaultTransactionManager")
072        public String createAuthorizationCode(OAuth2Authentication authentication) {
073                String code = generator.generate();
074
075                // attach the authorization so that we can look it up later
076                AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity();
077                authHolder.setAuthentication(authentication);
078                authHolder = authenticationHolderRepository.save(authHolder);
079
080                // set the auth code to expire
081                Date expiration = new Date(System.currentTimeMillis() + (getAuthCodeExpirationSeconds() * 1000L));
082
083                AuthorizationCodeEntity entity = new AuthorizationCodeEntity(code, authHolder, expiration);
084                repository.save(entity);
085
086                return code;
087        }
088
089        /**
090         * Consume a given authorization code.
091         * Match the provided string to an AuthorizationCodeEntity. If one is found, return
092         * the authentication associated with the code. If one is not found, throw an
093         * InvalidGrantException.
094         *
095         * @param code          the authorization code
096         * @return                      the authentication that made the original request
097         * @throws                      InvalidGrantException, if an AuthorizationCodeEntity is not found with the given value
098         */
099        @Override
100        public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException {
101
102                AuthorizationCodeEntity result = repository.getByCode(code);
103
104                if (result == null) {
105                        throw new InvalidGrantException("JpaAuthorizationCodeRepository: no authorization code found for value " + code);
106                }
107
108                OAuth2Authentication auth = result.getAuthenticationHolder().getAuthentication();
109
110                repository.remove(result);
111
112                return auth;
113        }
114
115        /**
116         * Find and remove all expired auth codes.
117         */
118        @Transactional(value="defaultTransactionManager")
119        public void clearExpiredAuthorizationCodes() {
120
121                new AbstractPageOperationTemplate<AuthorizationCodeEntity>("clearExpiredAuthorizationCodes"){
122                        @Override
123                        public Collection<AuthorizationCodeEntity> fetchPage() {
124                                return repository.getExpiredCodes();
125                        }
126
127                        @Override
128                        protected void doOperation(AuthorizationCodeEntity item) {
129                                repository.remove(item);
130                        }
131                }.execute();
132        }
133
134        /**
135         * @return the repository
136         */
137        public AuthorizationCodeRepository getRepository() {
138                return repository;
139        }
140
141        /**
142         * @param repository the repository to set
143         */
144        public void setRepository(AuthorizationCodeRepository repository) {
145                this.repository = repository;
146        }
147
148        /**
149         * @return the authCodeExpirationSeconds
150         */
151        public int getAuthCodeExpirationSeconds() {
152                return authCodeExpirationSeconds;
153        }
154
155        /**
156         * @param authCodeExpirationSeconds the authCodeExpirationSeconds to set
157         */
158        public void setAuthCodeExpirationSeconds(int authCodeExpirationSeconds) {
159                this.authCodeExpirationSeconds = authCodeExpirationSeconds;
160        }
161
162}