001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 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 *******************************************************************************/ 016 017package org.mitre.oauth2.service.impl; 018 019import java.util.Collection; 020import java.util.Date; 021import java.util.Map; 022import java.util.Set; 023import java.util.UUID; 024 025import org.mitre.data.AbstractPageOperationTemplate; 026import org.mitre.oauth2.model.AuthenticationHolderEntity; 027import org.mitre.oauth2.model.ClientDetailsEntity; 028import org.mitre.oauth2.model.DeviceCode; 029import org.mitre.oauth2.repository.impl.DeviceCodeRepository; 030import org.mitre.oauth2.service.DeviceCodeService; 031import org.springframework.beans.factory.annotation.Autowired; 032import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; 033import org.springframework.security.oauth2.provider.ClientDetails; 034import org.springframework.security.oauth2.provider.OAuth2Authentication; 035import org.springframework.stereotype.Service; 036import org.springframework.transaction.annotation.Transactional; 037 038/** 039 * @author jricher 040 * 041 */ 042@Service("defaultDeviceCodeService") 043public class DefaultDeviceCodeService implements DeviceCodeService { 044 045 @Autowired 046 private DeviceCodeRepository repository; 047 048 private RandomValueStringGenerator randomGenerator = new RandomValueStringGenerator(); 049 050 /* (non-Javadoc) 051 * @see org.mitre.oauth2.service.DeviceCodeService#save(org.mitre.oauth2.model.DeviceCode) 052 */ 053 @Override 054 public DeviceCode createNewDeviceCode(Set<String> requestedScopes, ClientDetailsEntity client, Map<String, String> parameters) { 055 056 // create a device code, should be big and random 057 String deviceCode = UUID.randomUUID().toString(); 058 059 // create a user code, should be random but small and typable, and always uppercase (lookup is case insensitive) 060 String userCode = randomGenerator.generate().toUpperCase(); 061 062 DeviceCode dc = new DeviceCode(deviceCode, userCode, requestedScopes, client.getClientId(), parameters); 063 064 if (client.getDeviceCodeValiditySeconds() != null) { 065 dc.setExpiration(new Date(System.currentTimeMillis() + client.getDeviceCodeValiditySeconds() * 1000L)); 066 } 067 068 dc.setApproved(false); 069 070 return repository.save(dc); 071 } 072 073 /* (non-Javadoc) 074 * @see org.mitre.oauth2.service.DeviceCodeService#lookUpByUserCode(java.lang.String) 075 */ 076 @Override 077 public DeviceCode lookUpByUserCode(String userCode) { 078 // always up-case the code for lookup 079 return repository.getByUserCode(userCode.toUpperCase()); 080 } 081 082 /* (non-Javadoc) 083 * @see org.mitre.oauth2.service.DeviceCodeService#approveDeviceCode(org.mitre.oauth2.model.DeviceCode) 084 */ 085 @Override 086 public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication auth) { 087 DeviceCode found = repository.getById(dc.getId()); 088 089 found.setApproved(true); 090 091 AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity(); 092 authHolder.setAuthentication(auth); 093 094 found.setAuthenticationHolder(authHolder); 095 096 return repository.save(found); 097 } 098 099 /* (non-Javadoc) 100 * @see org.mitre.oauth2.service.DeviceCodeService#consumeDeviceCode(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails) 101 */ 102 @Override 103 public DeviceCode findDeviceCode(String deviceCode, ClientDetails client) { 104 DeviceCode found = repository.getByDeviceCode(deviceCode); 105 106 if (found != null) { 107 if (found.getClientId().equals(client.getClientId())) { 108 // make sure the client matches, if so, we're good 109 return found; 110 } else { 111 // if the clients don't match, pretend the code wasn't found 112 return null; 113 } 114 } else { 115 // didn't find the code, return null 116 return null; 117 } 118 119 } 120 121 122 123 /* (non-Javadoc) 124 * @see org.mitre.oauth2.service.DeviceCodeService#clearExpiredDeviceCodes() 125 */ 126 @Override 127 @Transactional(value="defaultTransactionManager") 128 public void clearExpiredDeviceCodes() { 129 130 new AbstractPageOperationTemplate<DeviceCode>("clearExpiredDeviceCodes"){ 131 @Override 132 public Collection<DeviceCode> fetchPage() { 133 return repository.getExpiredCodes(); 134 } 135 136 @Override 137 protected void doOperation(DeviceCode item) { 138 repository.remove(item); 139 } 140 }.execute(); 141 } 142 143 /* (non-Javadoc) 144 * @see org.mitre.oauth2.service.DeviceCodeService#clearDeviceCode(java.lang.String, org.springframework.security.oauth2.provider.ClientDetails) 145 */ 146 @Override 147 public void clearDeviceCode(String deviceCode, ClientDetails client) { 148 DeviceCode found = findDeviceCode(deviceCode, client); 149 150 if (found != null) { 151 // make sure it's not used twice 152 repository.remove(found); 153 } 154 155 } 156 157}