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}