diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java index 8f7486b9f..a886729c3 100644 --- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/DeviceCode.java @@ -33,6 +33,8 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.MapKeyColumn; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Temporal; @@ -42,8 +44,21 @@ import javax.persistence.Temporal; */ @Entity @Table(name = "device_code") +@NamedQueries({ + @NamedQuery(name = DeviceCode.QUERY_BY_USER_CODE, query = "select d from DeviceCode d where d.userCode = :" + DeviceCode.PARAM_USER_CODE), + @NamedQuery(name = DeviceCode.QUERY_BY_DEVICE_CODE, query = "select d from DeviceCode d where d.deviceCode = :" + DeviceCode.PARAM_DEVICE_CODE), + @NamedQuery(name = DeviceCode.QUERY_EXPIRED_BY_DATE, query = "select d from DeviceCode d where d.expiration <= :" + DeviceCode.PARAM_DATE) +}) public class DeviceCode { + public static final String QUERY_BY_USER_CODE = "DeviceCode.queryByUserCode"; + public static final String QUERY_BY_DEVICE_CODE = "DeviceCode.queryByDeviceCode"; + public static final String QUERY_EXPIRED_BY_DATE = "DeviceCode.queryExpiredByDate"; + + public static final String PARAM_USER_CODE = "userCode"; + public static final String PARAM_DEVICE_CODE = "deviceCode"; + public static final String PARAM_DATE = "date"; + private Long id; private String deviceCode; private String userCode; diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java new file mode 100644 index 000000000..955cf6773 --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/oauth2/repository/impl/DeviceCodeRepository.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * Licensed 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 + * + * 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.mitre.oauth2.repository.impl; + +import java.util.Set; + +import org.mitre.oauth2.model.DeviceCode; + +/** + * @author jricher + * + */ +public interface DeviceCodeRepository { + + /** + * @param id + * @return + */ + public DeviceCode getById(Long id); + + /** + * @param deviceCode + * @return + */ + public DeviceCode getByDeviceCode(String deviceCode); + + /** + * @param scope + */ + public void remove(DeviceCode scope); + + /** + * @param scope + * @return + */ + public DeviceCode save(DeviceCode scope); + + /** + * @param userCode + * @return + */ + public DeviceCode getByUserCode(String userCode); + +} diff --git a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql index 8f5e69ce7..27d9c698d 100644 --- a/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql +++ b/openid-connect-server-webapp/src/main/resources/db/hsql/hsql_database_tables.sql @@ -131,7 +131,7 @@ CREATE TABLE IF NOT EXISTS client_details ( dynamically_registered BOOLEAN DEFAULT false NOT NULL, allow_introspection BOOLEAN DEFAULT false NOT NULL, id_token_validity_seconds BIGINT DEFAULT 600 NOT NULL, - device_code_validity_seconds BIGINT DEFAULT 600 NOT NULL, + device_code_validity_seconds BIGINT, client_id VARCHAR(256), client_secret VARCHAR(2048), diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java new file mode 100644 index 000000000..a72555040 --- /dev/null +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/repository/impl/JpaDeviceCodeRepository.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright 2017 The MITRE Corporation + * and the MIT Internet Trust Consortium + * + * Licensed 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 + * + * 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.mitre.oauth2.repository.impl; + +import static org.mitre.util.jpa.JpaUtil.getSingleResult; +import static org.mitre.util.jpa.JpaUtil.saveOrUpdate; + +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; + +import org.mitre.oauth2.model.DeviceCode; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +/** + * @author jricher + * + */ +@Repository("jpaDeviceCodeRepository") +public class JpaDeviceCodeRepository implements DeviceCodeRepository { + + @PersistenceContext(unitName="defaultPersistenceUnit") + private EntityManager em; + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode getById(Long id) { + return em.find(DeviceCode.class, id); + } + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode getByUserCode(String value) { + TypedQuery query = em.createNamedQuery(DeviceCode.QUERY_BY_USER_CODE, DeviceCode.class); + query.setParameter(DeviceCode.PARAM_USER_CODE, value); + return getSingleResult(query.getResultList()); + } + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode getByDeviceCode(String value) { + TypedQuery query = em.createNamedQuery(DeviceCode.QUERY_BY_DEVICE_CODE, DeviceCode.class); + query.setParameter(DeviceCode.PARAM_DEVICE_CODE, value); + return getSingleResult(query.getResultList()); + } + + /* (non-Javadoc) + */ + @Override + @Transactional(value="defaultTransactionManager") + public void remove(DeviceCode scope) { + DeviceCode found = getById(scope.getId()); + + if (found != null) { + em.remove(found); + } + + } + + /* (non-Javadoc) + * @see org.mitre.oauth2.repository.SystemScopeRepository#save(org.mitre.oauth2.model.SystemScope) + */ + @Override + @Transactional(value="defaultTransactionManager") + public DeviceCode save(DeviceCode scope) { + return saveOrUpdate(scope.getId(), em, scope); + } + +} diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java similarity index 75% rename from openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java rename to openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java index eb522c5b6..0ef367778 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/InMemoryDeviceCodeService.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/service/impl/DefaultDeviceCodeService.java @@ -18,15 +18,15 @@ package org.mitre.oauth2.service.impl; import java.util.Date; -import java.util.HashSet; import java.util.Map; import java.util.Set; import org.mitre.oauth2.model.AuthenticationHolderEntity; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.DeviceCode; +import org.mitre.oauth2.repository.impl.DeviceCodeRepository; import org.mitre.oauth2.service.DeviceCodeService; -import org.springframework.security.core.Authentication; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.stereotype.Service; @@ -35,10 +35,11 @@ import org.springframework.stereotype.Service; * @author jricher * */ -@Service -public class InMemoryDeviceCodeService implements DeviceCodeService { +@Service("defaultDeviceCodeService") +public class DefaultDeviceCodeService implements DeviceCodeService { - private Set codes = new HashSet<>(); + @Autowired + private DeviceCodeRepository repository; /* (non-Javadoc) * @see org.mitre.oauth2.service.DeviceCodeService#save(org.mitre.oauth2.model.DeviceCode) @@ -54,8 +55,7 @@ public class InMemoryDeviceCodeService implements DeviceCodeService { dc.setApproved(false); - codes.add(dc); - return dc; + return repository.save(dc); } /* (non-Javadoc) @@ -63,12 +63,7 @@ public class InMemoryDeviceCodeService implements DeviceCodeService { */ @Override public DeviceCode lookUpByUserCode(String userCode) { - for (DeviceCode dc : codes) { - if (dc.getUserCode().equals(userCode)) { - return dc; - } - } - return null; + return repository.getByUserCode(userCode); } /* (non-Javadoc) @@ -76,13 +71,16 @@ public class InMemoryDeviceCodeService implements DeviceCodeService { */ @Override public DeviceCode approveDeviceCode(DeviceCode dc, OAuth2Authentication auth) { - dc.setApproved(true); + DeviceCode found = repository.getById(dc.getId()); + + found.setApproved(true); AuthenticationHolderEntity authHolder = new AuthenticationHolderEntity(); authHolder.setAuthentication(auth); - dc.setAuthenticationHolder(authHolder); + + found.setAuthenticationHolder(authHolder); - return dc; + return repository.save(found); } /* (non-Javadoc) @@ -90,13 +88,19 @@ public class InMemoryDeviceCodeService implements DeviceCodeService { */ @Override public DeviceCode consumeDeviceCode(String deviceCode, ClientDetails client) { - for (DeviceCode dc : codes) { - if (dc.getDeviceCode().equals(deviceCode) && dc.getClientId().equals(client.getClientId())) { - codes.remove(dc); - return dc; - } + DeviceCode found = repository.getByDeviceCode(deviceCode); + + // make sure it's not used twice + repository.remove(found); + + if (found.getClientId().equals(client.getClientId())) { + // make sure the client matches, if so, we're good + return found; + } else { + // if the clients don't match, pretend the code wasn't found + return null; } - return null; + } } diff --git a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java index 31028ef44..2ba7a808b 100644 --- a/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/oauth2/web/DeviceEndpoint.java @@ -19,6 +19,7 @@ package org.mitre.oauth2.web; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -147,12 +148,15 @@ public class DeviceEndpoint { DeviceCode dc = deviceCodeService.createNewDeviceCode(deviceCode, userCode, requestedScopes, client, parameters); - model.put(JsonEntityView.ENTITY, ImmutableMap.of( - "device_code", deviceCode, - "user_code", userCode, - "verification_uri", config.getIssuer() + URL, - "expires_in", client.getDeviceCodeValiditySeconds() - )); + Map response = new HashMap<>(); + response.put("device_code", deviceCode); + response.put("user_code", userCode); + response.put("verification_uri", config.getIssuer() + USER_URL); + if (client.getDeviceCodeValiditySeconds() != null) { + response.put("expires_in", client.getDeviceCodeValiditySeconds()); + } + + model.put(JsonEntityView.ENTITY, response); return JsonEntityView.VIEWNAME;