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}