/******************************************************************************* * Copyright 2018 The MIT Internet Trust Consortium * * Portions copyright 2011-2013 The MITRE Corporation * * 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.model; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.persistence.Basic; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.Convert; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import org.mitre.oauth2.model.convert.JWEAlgorithmStringConverter; import org.mitre.oauth2.model.convert.JWEEncryptionMethodStringConverter; import org.mitre.oauth2.model.convert.JWKSetStringConverter; import org.mitre.oauth2.model.convert.JWSAlgorithmStringConverter; import org.mitre.oauth2.model.convert.JWTStringConverter; import org.mitre.oauth2.model.convert.PKCEAlgorithmStringConverter; import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.provider.ClientDetails; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jwt.JWT; /** * @author jricher * */ @Entity @Table(name = "client_details") @NamedQueries({ @NamedQuery(name = ClientDetailsEntity.QUERY_ALL, query = "SELECT c FROM ClientDetailsEntity c"), @NamedQuery(name = ClientDetailsEntity.QUERY_BY_CLIENT_ID, query = "select c from ClientDetailsEntity c where c.clientId = :" + ClientDetailsEntity.PARAM_CLIENT_ID) }) public class ClientDetailsEntity implements ClientDetails { public static final String QUERY_BY_CLIENT_ID = "ClientDetailsEntity.getByClientId"; public static final String QUERY_ALL = "ClientDetailsEntity.findAll"; public static final String PARAM_CLIENT_ID = "clientId"; private static final int DEFAULT_ID_TOKEN_VALIDITY_SECONDS = 600; private static final long serialVersionUID = -1617727085733786296L; private Long id; /** Fields from the OAuth2 Dynamic Registration Specification */ private String clientId = null; // client_id private String clientSecret = null; // client_secret private Set redirectUris = new HashSet<>(); // redirect_uris private String clientName; // client_name private String clientUri; // client_uri private String logoUri; // logo_uri private Set contacts; // contacts private String tosUri; // tos_uri private AuthMethod tokenEndpointAuthMethod = AuthMethod.SECRET_BASIC; // token_endpoint_auth_method private Set scope = new HashSet<>(); // scope private Set grantTypes = new HashSet<>(); // grant_types private Set responseTypes = new HashSet<>(); // response_types private String policyUri; private String jwksUri; // URI pointer to keys private JWKSet jwks; // public key stored by value private String softwareId; private String softwareVersion; /** Fields from OIDC Client Registration Specification **/ private AppType applicationType; // application_type private String sectorIdentifierUri; // sector_identifier_uri private SubjectType subjectType; // subject_type private JWSAlgorithm requestObjectSigningAlg = null; // request_object_signing_alg private JWSAlgorithm userInfoSignedResponseAlg = null; // user_info_signed_response_alg private JWEAlgorithm userInfoEncryptedResponseAlg = null; // user_info_encrypted_response_alg private EncryptionMethod userInfoEncryptedResponseEnc = null; // user_info_encrypted_response_enc private JWSAlgorithm idTokenSignedResponseAlg = null; // id_token_signed_response_alg private JWEAlgorithm idTokenEncryptedResponseAlg = null; // id_token_encrypted_response_alg private EncryptionMethod idTokenEncryptedResponseEnc = null; // id_token_encrypted_response_enc private JWSAlgorithm tokenEndpointAuthSigningAlg = null; // token_endpoint_auth_signing_alg private Integer defaultMaxAge; // default_max_age private Boolean requireAuthTime; // require_auth_time private Set defaultACRvalues; // default_acr_values private String initiateLoginUri; // initiate_login_uri private Set postLogoutRedirectUris; // post_logout_redirect_uris private Set requestUris; // request_uris /** Fields to support the ClientDetails interface **/ private Set authorities = new HashSet<>(); private Integer accessTokenValiditySeconds = 0; // in seconds private Integer refreshTokenValiditySeconds = 0; // in seconds private Set resourceIds = new HashSet<>(); private Map additionalInformation = new HashMap<>(); /** Our own fields **/ private String clientDescription = ""; // human-readable description private boolean reuseRefreshToken = true; // do we let someone reuse a refresh token? private boolean dynamicallyRegistered = false; // was this client dynamically registered? private boolean allowIntrospection = false; // do we let this client call the introspection endpoint? private Integer idTokenValiditySeconds; //timeout for id tokens private Date createdAt; // time the client was created private boolean clearAccessTokensOnRefresh = true; // do we clear access tokens on refresh? private Integer deviceCodeValiditySeconds; // timeout for device codes /** fields for UMA */ private Set claimsRedirectUris; /** Software statement **/ private JWT softwareStatement; /** PKCE **/ private PKCEAlgorithm codeChallengeMethod; public enum AuthMethod { SECRET_POST("client_secret_post"), SECRET_BASIC("client_secret_basic"), SECRET_JWT("client_secret_jwt"), PRIVATE_KEY("private_key_jwt"), NONE("none"); private final String value; // map to aid reverse lookup private static final Map lookup = new HashMap<>(); static { for (AuthMethod a : AuthMethod.values()) { lookup.put(a.getValue(), a); } } AuthMethod(String value) { this.value = value; } public String getValue() { return value; } public static AuthMethod getByValue(String value) { return lookup.get(value); } } public enum AppType { WEB("web"), NATIVE("native"); private final String value; // map to aid reverse lookup private static final Map lookup = new HashMap<>(); static { for (AppType a : AppType.values()) { lookup.put(a.getValue(), a); } } AppType(String value) { this.value = value; } public String getValue() { return value; } public static AppType getByValue(String value) { return lookup.get(value); } } public enum SubjectType { PAIRWISE("pairwise"), PUBLIC("public"); private final String value; // map to aid reverse lookup private static final Map lookup = new HashMap<>(); static { for (SubjectType u : SubjectType.values()) { lookup.put(u.getValue(), u); } } SubjectType(String value) { this.value = value; } public String getValue() { return value; } public static SubjectType getByValue(String value) { return lookup.get(value); } } /** * Create a blank ClientDetailsEntity */ public ClientDetailsEntity() { } @PrePersist @PreUpdate private void prePersist() { // make sure that ID tokens always time out, default to 5 minutes if (getIdTokenValiditySeconds() == null) { setIdTokenValiditySeconds(DEFAULT_ID_TOKEN_VALIDITY_SECONDS); } } /** * @return the id */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") public Long getId() { return id; } /** * * @param id the id to set */ public void setId(Long id) { this.id = id; } /** * @return the clientDescription */ @Basic @Column(name="client_description") public String getClientDescription() { return clientDescription; } /** * @param clientDescription Human-readable long description of the client (optional) */ public void setClientDescription(String clientDescription) { this.clientDescription = clientDescription; } /** * @return the allowRefresh */ @Transient public boolean isAllowRefresh() { if (grantTypes != null) { return getAuthorizedGrantTypes().contains("refresh_token"); } else { return false; // if there are no grants, we can't be refreshing them, can we? } } @Basic @Column(name="reuse_refresh_tokens") public boolean isReuseRefreshToken() { return reuseRefreshToken; } public void setReuseRefreshToken(boolean reuseRefreshToken) { this.reuseRefreshToken = reuseRefreshToken; } /** * Number of seconds ID token is valid for. MUST be a positive integer, can not be null. * * @return the idTokenValiditySeconds */ @Basic @Column(name="id_token_validity_seconds") public Integer getIdTokenValiditySeconds() { return idTokenValiditySeconds; } /** * @param idTokenValiditySeconds the idTokenValiditySeconds to set */ public void setIdTokenValiditySeconds(Integer idTokenValiditySeconds) { this.idTokenValiditySeconds = idTokenValiditySeconds; } /** * @return the dynamicallyRegistered */ @Basic @Column(name="dynamically_registered") public boolean isDynamicallyRegistered() { return dynamicallyRegistered; } /** * @param dynamicallyRegistered the dynamicallyRegistered to set */ public void setDynamicallyRegistered(boolean dynamicallyRegistered) { this.dynamicallyRegistered = dynamicallyRegistered; } /** * @return the allowIntrospection */ @Basic @Column(name="allow_introspection") public boolean isAllowIntrospection() { return allowIntrospection; } /** * @param allowIntrospection the allowIntrospection to set */ public void setAllowIntrospection(boolean allowIntrospection) { this.allowIntrospection = allowIntrospection; } /** * */ @Override @Transient public boolean isSecretRequired() { if (getTokenEndpointAuthMethod() != null && (getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_BASIC) || getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST) || getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT))) { return true; } else { return false; } } /** * If the scope list is not null or empty, then this client has been scoped. */ @Override @Transient public boolean isScoped() { return getScope() != null && !getScope().isEmpty(); } /** * @return the clientId */ @Basic @Override @Column(name="client_id") public String getClientId() { return clientId; } /** * @param clientId The OAuth2 client_id, must be unique to this client */ public void setClientId(String clientId) { this.clientId = clientId; } /** * @return the clientSecret */ @Basic @Override @Column(name="client_secret") public String getClientSecret() { return clientSecret; } /** * @param clientSecret the OAuth2 client_secret (optional) */ public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; } /** * @return the scope */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_scope", joinColumns=@JoinColumn(name="owner_id") ) @Override @Column(name="scope") public Set getScope() { return scope; } /** * @param scope the set of scopes allowed to be issued to this client */ public void setScope(Set scope) { this.scope = scope; } /** * @return the authorizedGrantTypes */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_grant_type", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="grant_type") public Set getGrantTypes() { return grantTypes; } /** * @param authorizedGrantTypes the OAuth2 grant types that this client is allowed to use */ public void setGrantTypes(Set grantTypes) { this.grantTypes = grantTypes; } /** * passthrough for SECOAUTH api */ @Override @Transient public Set getAuthorizedGrantTypes() { return getGrantTypes(); } /** * @return the authorities */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_authority", joinColumns=@JoinColumn(name="owner_id") ) @Override @Convert(converter = SimpleGrantedAuthorityStringConverter.class) @Column(name="authority") public Set getAuthorities() { return authorities; } /** * @param authorities the Spring Security authorities this client is given */ public void setAuthorities(Set authorities) { this.authorities = authorities; } @Override @Basic @Column(name="access_token_validity_seconds") public Integer getAccessTokenValiditySeconds() { return accessTokenValiditySeconds; } /** * @param accessTokenTimeout the accessTokenTimeout to set */ public void setAccessTokenValiditySeconds(Integer accessTokenValiditySeconds) { this.accessTokenValiditySeconds = accessTokenValiditySeconds; } @Override @Basic @Column(name="refresh_token_validity_seconds") public Integer getRefreshTokenValiditySeconds() { return refreshTokenValiditySeconds; } /** * @param refreshTokenTimeout Lifetime of refresh tokens, in seconds (optional - leave null for no timeout) */ public void setRefreshTokenValiditySeconds(Integer refreshTokenValiditySeconds) { this.refreshTokenValiditySeconds = refreshTokenValiditySeconds; } /** * @return the registeredRedirectUri */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_redirect_uri", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="redirect_uri") public Set getRedirectUris() { return redirectUris; } /** * @param registeredRedirectUri the registeredRedirectUri to set */ public void setRedirectUris(Set redirectUris) { this.redirectUris = redirectUris; } /** * Pass-through method to fulfill the ClientDetails interface with a bad name */ @Override @Transient public Set getRegisteredRedirectUri() { return getRedirectUris(); } /** * @return the resourceIds */ @Override @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_resource", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="resource_id") public Set getResourceIds() { return resourceIds; } /** * @param resourceIds the resourceIds to set */ public void setResourceIds(Set resourceIds) { this.resourceIds = resourceIds; } /** * This library does not make use of this field, so it is not * stored using our persistence layer. * * However, it's somehow required by SECOUATH. * * @return an empty map */ @Override @Transient public Map getAdditionalInformation() { return this.additionalInformation; } @Enumerated(EnumType.STRING) @Column(name="application_type") public AppType getApplicationType() { return applicationType; } public void setApplicationType(AppType applicationType) { this.applicationType = applicationType; } @Basic @Column(name="client_name") public String getClientName() { return clientName; } public void setClientName(String clientName) { this.clientName = clientName; } @Enumerated(EnumType.STRING) @Column(name="token_endpoint_auth_method") public AuthMethod getTokenEndpointAuthMethod() { return tokenEndpointAuthMethod; } public void setTokenEndpointAuthMethod(AuthMethod tokenEndpointAuthMethod) { this.tokenEndpointAuthMethod = tokenEndpointAuthMethod; } @Enumerated(EnumType.STRING) @Column(name="subject_type") public SubjectType getSubjectType() { return subjectType; } public void setSubjectType(SubjectType subjectType) { this.subjectType = subjectType; } @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_contact", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="contact") public Set getContacts() { return contacts; } public void setContacts(Set contacts) { this.contacts = contacts; } @Basic @Column(name="logo_uri") public String getLogoUri() { return logoUri; } public void setLogoUri(String logoUri) { this.logoUri = logoUri; } @Basic @Column(name="policy_uri") public String getPolicyUri() { return policyUri; } public void setPolicyUri(String policyUri) { this.policyUri = policyUri; } /** * @return the clientUrl */ @Basic @Column(name="client_uri") public String getClientUri() { return clientUri; } /** * @param clientUrl the clientUrl to set */ public void setClientUri(String clientUri) { this.clientUri = clientUri; } /** * @return the tosUrl */ @Basic @Column(name="tos_uri") public String getTosUri() { return tosUri; } /** * @param tosUrl the tosUrl to set */ public void setTosUri(String tosUri) { this.tosUri = tosUri; } @Basic @Column(name="jwks_uri") public String getJwksUri() { return jwksUri; } public void setJwksUri(String jwksUri) { this.jwksUri = jwksUri; } /** * @return the jwks */ @Basic @Column(name="jwks") @Convert(converter = JWKSetStringConverter.class) public JWKSet getJwks() { return jwks; } /** * @param jwks the jwks to set */ public void setJwks(JWKSet jwks) { this.jwks = jwks; } @Basic @Column(name="sector_identifier_uri") public String getSectorIdentifierUri() { return sectorIdentifierUri; } public void setSectorIdentifierUri(String sectorIdentifierUri) { this.sectorIdentifierUri = sectorIdentifierUri; } @Basic @Column(name = "request_object_signing_alg") @Convert(converter = JWSAlgorithmStringConverter.class) public JWSAlgorithm getRequestObjectSigningAlg() { return requestObjectSigningAlg; } public void setRequestObjectSigningAlg(JWSAlgorithm requestObjectSigningAlg) { this.requestObjectSigningAlg = requestObjectSigningAlg; } @Basic @Column(name = "user_info_signed_response_alg") @Convert(converter = JWSAlgorithmStringConverter.class) public JWSAlgorithm getUserInfoSignedResponseAlg() { return userInfoSignedResponseAlg; } public void setUserInfoSignedResponseAlg(JWSAlgorithm userInfoSignedResponseAlg) { this.userInfoSignedResponseAlg = userInfoSignedResponseAlg; } @Basic @Column(name = "user_info_encrypted_response_alg") @Convert(converter = JWEAlgorithmStringConverter.class) public JWEAlgorithm getUserInfoEncryptedResponseAlg() { return userInfoEncryptedResponseAlg; } public void setUserInfoEncryptedResponseAlg(JWEAlgorithm userInfoEncryptedResponseAlg) { this.userInfoEncryptedResponseAlg = userInfoEncryptedResponseAlg; } @Basic @Column(name = "user_info_encrypted_response_enc") @Convert(converter = JWEEncryptionMethodStringConverter.class) public EncryptionMethod getUserInfoEncryptedResponseEnc() { return userInfoEncryptedResponseEnc; } public void setUserInfoEncryptedResponseEnc(EncryptionMethod userInfoEncryptedResponseEnc) { this.userInfoEncryptedResponseEnc = userInfoEncryptedResponseEnc; } @Basic @Column(name="id_token_signed_response_alg") @Convert(converter = JWSAlgorithmStringConverter.class) public JWSAlgorithm getIdTokenSignedResponseAlg() { return idTokenSignedResponseAlg; } public void setIdTokenSignedResponseAlg(JWSAlgorithm idTokenSignedResponseAlg) { this.idTokenSignedResponseAlg = idTokenSignedResponseAlg; } @Basic @Column(name = "id_token_encrypted_response_alg") @Convert(converter = JWEAlgorithmStringConverter.class) public JWEAlgorithm getIdTokenEncryptedResponseAlg() { return idTokenEncryptedResponseAlg; } public void setIdTokenEncryptedResponseAlg(JWEAlgorithm idTokenEncryptedResponseAlg) { this.idTokenEncryptedResponseAlg = idTokenEncryptedResponseAlg; } @Basic @Column(name = "id_token_encrypted_response_enc") @Convert(converter = JWEEncryptionMethodStringConverter.class) public EncryptionMethod getIdTokenEncryptedResponseEnc() { return idTokenEncryptedResponseEnc; } public void setIdTokenEncryptedResponseEnc(EncryptionMethod idTokenEncryptedResponseEnc) { this.idTokenEncryptedResponseEnc = idTokenEncryptedResponseEnc; } @Basic @Column(name="token_endpoint_auth_signing_alg") @Convert(converter = JWSAlgorithmStringConverter.class) public JWSAlgorithm getTokenEndpointAuthSigningAlg() { return tokenEndpointAuthSigningAlg; } public void setTokenEndpointAuthSigningAlg(JWSAlgorithm tokenEndpointAuthSigningAlg) { this.tokenEndpointAuthSigningAlg = tokenEndpointAuthSigningAlg; } @Basic @Column(name="default_max_age") public Integer getDefaultMaxAge() { return defaultMaxAge; } public void setDefaultMaxAge(Integer defaultMaxAge) { this.defaultMaxAge = defaultMaxAge; } @Basic @Column(name="require_auth_time") public Boolean getRequireAuthTime() { return requireAuthTime; } public void setRequireAuthTime(Boolean requireAuthTime) { this.requireAuthTime = requireAuthTime; } /** * @return the responseTypes */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_response_type", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="response_type") public Set getResponseTypes() { return responseTypes; } /** * @param responseTypes the responseTypes to set */ public void setResponseTypes(Set responseTypes) { this.responseTypes = responseTypes; } /** * @return the defaultACRvalues */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_default_acr_value", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="default_acr_value") public Set getDefaultACRvalues() { return defaultACRvalues; } /** * @param defaultACRvalues the defaultACRvalues to set */ public void setDefaultACRvalues(Set defaultACRvalues) { this.defaultACRvalues = defaultACRvalues; } /** * @return the initiateLoginUri */ @Basic @Column(name="initiate_login_uri") public String getInitiateLoginUri() { return initiateLoginUri; } /** * @param initiateLoginUri the initiateLoginUri to set */ public void setInitiateLoginUri(String initiateLoginUri) { this.initiateLoginUri = initiateLoginUri; } /** * @return the postLogoutRedirectUri */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_post_logout_redirect_uri", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="post_logout_redirect_uri") public Set getPostLogoutRedirectUris() { return postLogoutRedirectUris; } /** * @param postLogoutRedirectUri the postLogoutRedirectUri to set */ public void setPostLogoutRedirectUris(Set postLogoutRedirectUri) { this.postLogoutRedirectUris = postLogoutRedirectUri; } /** * @return the requestUris */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_request_uri", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="request_uri") public Set getRequestUris() { return requestUris; } /** * @param requestUris the requestUris to set */ public void setRequestUris(Set requestUris) { this.requestUris = requestUris; } /** * @return the createdAt */ @Temporal(TemporalType.TIMESTAMP) @Column(name="created_at") public Date getCreatedAt() { return createdAt; } /** * @param createdAt the createdAt to set */ public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; } /** * Our framework doesn't use this construct, we use WhitelistedSites and ApprovedSites instead. */ @Override public boolean isAutoApprove(String scope) { return false; } /** * @return the clearAccessTokensOnRefresh */ @Basic @Column(name = "clear_access_tokens_on_refresh") public boolean isClearAccessTokensOnRefresh() { return clearAccessTokensOnRefresh; } /** * @param clearAccessTokensOnRefresh the clearAccessTokensOnRefresh to set */ public void setClearAccessTokensOnRefresh(boolean clearAccessTokensOnRefresh) { this.clearAccessTokensOnRefresh = clearAccessTokensOnRefresh; } /** * @return the claimsRedirectUris */ @ElementCollection(fetch = FetchType.EAGER) @CollectionTable( name="client_claims_redirect_uri", joinColumns=@JoinColumn(name="owner_id") ) @Column(name="redirect_uri") public Set getClaimsRedirectUris() { return claimsRedirectUris; } /** * @param claimsRedirectUris the claimsRedirectUris to set */ public void setClaimsRedirectUris(Set claimsRedirectUris) { this.claimsRedirectUris = claimsRedirectUris; } /** * @return the softwareStatement */ @Basic @Column(name = "software_statement") @Convert(converter = JWTStringConverter.class) public JWT getSoftwareStatement() { return softwareStatement; } /** * @param softwareStatement the softwareStatement to set */ public void setSoftwareStatement(JWT softwareStatement) { this.softwareStatement = softwareStatement; } /** * @return the codeChallengeMethod */ @Basic @Column(name = "code_challenge_method") @Convert(converter = PKCEAlgorithmStringConverter.class) public PKCEAlgorithm getCodeChallengeMethod() { return codeChallengeMethod; } /** * @param codeChallengeMethod the codeChallengeMethod to set */ public void setCodeChallengeMethod(PKCEAlgorithm codeChallengeMethod) { this.codeChallengeMethod = codeChallengeMethod; } /** * @return the deviceCodeValiditySeconds */ @Basic @Column(name="device_code_validity_seconds") public Integer getDeviceCodeValiditySeconds() { return deviceCodeValiditySeconds; } /** * @param deviceCodeValiditySeconds the deviceCodeValiditySeconds to set */ public void setDeviceCodeValiditySeconds(Integer deviceCodeValiditySeconds) { this.deviceCodeValiditySeconds = deviceCodeValiditySeconds; } /** * @return the softwareId */ @Basic @Column(name="software_id") public String getSoftwareId() { return softwareId; } /** * @param softwareId the softwareId to set */ public void setSoftwareId(String softwareId) { this.softwareId = softwareId; } /** * @return the softwareVersion */ @Basic @Column(name="software_version") public String getSoftwareVersion() { return softwareVersion; } /** * @param softwareVersion the softwareVersion to set */ public void setSoftwareVersion(String softwareVersion) { this.softwareVersion = softwareVersion; } }