blob: 0e118d51d9f5febc39255e07ef5c1a6b2f7225cc [file] [log] [blame]
margarethaa452c5e2018-04-25 22:48:09 +02001package de.ids_mannheim.korap.oauth2.dao;
margaretha31a9f522018-04-03 20:40:45 +02002
margaretha230effb2018-11-29 17:28:18 +01003import java.time.ZoneId;
4import java.time.ZonedDateTime;
5import java.util.List;
6
margaretha6e796842023-08-17 15:10:45 +02007import jakarta.persistence.EntityManager;
8import jakarta.persistence.NoResultException;
9import jakarta.persistence.PersistenceContext;
10import jakarta.persistence.Query;
11import jakarta.persistence.TypedQuery;
12import jakarta.persistence.criteria.CriteriaBuilder;
13import jakarta.persistence.criteria.CriteriaQuery;
14import jakarta.persistence.criteria.Join;
15import jakarta.persistence.criteria.Predicate;
16import jakarta.persistence.criteria.Root;
margaretha31a9f522018-04-03 20:40:45 +020017
margarethad7163122022-04-11 09:42:41 +020018import org.springframework.beans.factory.annotation.Autowired;
margaretha31a9f522018-04-03 20:40:45 +020019import org.springframework.stereotype.Repository;
20import org.springframework.transaction.annotation.Transactional;
21
margarethad7163122022-04-11 09:42:41 +020022import com.fasterxml.jackson.databind.JsonNode;
23
margaretha230effb2018-11-29 17:28:18 +010024import de.ids_mannheim.korap.config.Attributes;
margarethad7163122022-04-11 09:42:41 +020025import de.ids_mannheim.korap.config.FullConfiguration;
margaretha31a9f522018-04-03 20:40:45 +020026import de.ids_mannheim.korap.exceptions.KustvaktException;
margaretha0e8f4e72018-04-05 14:11:52 +020027import de.ids_mannheim.korap.exceptions.StatusCodes;
margarethaa452c5e2018-04-25 22:48:09 +020028import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
margaretha0afd44a2020-02-05 10:49:21 +010029import de.ids_mannheim.korap.oauth2.entity.AccessToken;
30import de.ids_mannheim.korap.oauth2.entity.AccessToken_;
margarethaa452c5e2018-04-25 22:48:09 +020031import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
32import de.ids_mannheim.korap.oauth2.entity.OAuth2Client_;
margaretha230effb2018-11-29 17:28:18 +010033import de.ids_mannheim.korap.oauth2.entity.RefreshToken;
34import de.ids_mannheim.korap.oauth2.entity.RefreshToken_;
margaretha31a9f522018-04-03 20:40:45 +020035import de.ids_mannheim.korap.utils.ParameterChecker;
36
margaretha0afd44a2020-02-05 10:49:21 +010037/**
38 * Manages database queries and transactions regarding OAuth2 clients.
margaretha398f4722019-01-09 19:07:20 +010039 *
40 * @author margaretha
41 *
42 */
margaretha31a9f522018-04-03 20:40:45 +020043@Transactional
44@Repository
45public class OAuth2ClientDao {
46
47 @PersistenceContext
48 private EntityManager entityManager;
margarethad7163122022-04-11 09:42:41 +020049 @Autowired
50 private FullConfiguration config;
margaretha31a9f522018-04-03 20:40:45 +020051
margaretha6d61a552018-04-10 19:26:44 +020052 public void registerClient (String id, String secretHashcode, String name,
Akrondc6d73d2020-04-15 16:40:04 +020053 OAuth2ClientType type, String url, String redirectURI,
margarethad7163122022-04-11 09:42:41 +020054 String registeredBy, String description, int refreshTokenExpiry,
55 JsonNode source) throws KustvaktException {
margaretha18259ce2021-05-03 17:31:58 +020056 ParameterChecker.checkStringValue(id, "client_id");
57 ParameterChecker.checkStringValue(name, "client_name");
58 ParameterChecker.checkObjectValue(type, "client_type");
59 ParameterChecker.checkStringValue(description, "client_description");
margarethad7cab212018-07-02 19:01:43 +020060 // ParameterChecker.checkStringValue(url, "client url");
61 // ParameterChecker.checkStringValue(redirectURI, "client
62 // redirect uri");
margaretha18259ce2021-05-03 17:31:58 +020063 ParameterChecker.checkStringValue(registeredBy, "registered_by");
margaretha31a9f522018-04-03 20:40:45 +020064
65 OAuth2Client client = new OAuth2Client();
66 client.setId(id);
67 client.setName(name);
margaretha6d61a552018-04-10 19:26:44 +020068 client.setSecret(secretHashcode);
margaretha31a9f522018-04-03 20:40:45 +020069 client.setType(type);
margaretha85273f12019-02-04 18:13:17 +010070 client.setUrl(url);
margaretha31a9f522018-04-03 20:40:45 +020071 client.setRedirectURI(redirectURI);
margaretha0e8f4e72018-04-05 14:11:52 +020072 client.setRegisteredBy(registeredBy);
margarethad7163122022-04-11 09:42:41 +020073 client.setRegistrationDate(ZonedDateTime.now());
margarethafb027f92018-04-23 20:00:13 +020074 client.setDescription(description);
margaretha4ff862a2022-06-03 12:40:21 +020075 if (source != null && !source.isNull()) {
76 if (type.equals(OAuth2ClientType.CONFIDENTIAL)) {
77 client.setSource(source.toString());
78 }
79 else {
80 throw new KustvaktException(StatusCodes.NOT_SUPPORTED,
81 "Only confidential plugins are supported.");
82 }
margarethad7163122022-04-11 09:42:41 +020083 }
84 else {
85 client.setPermitted(true);
86 }
margaretha7d7e25e2022-05-27 08:41:25 +020087 if (refreshTokenExpiry <= 0) {
margaretha35e1ca22023-11-16 22:00:01 +010088 if (type.equals(OAuth2ClientType.CONFIDENTIAL)) {
margaretha7d7e25e2022-05-27 08:41:25 +020089 refreshTokenExpiry = config.getRefreshTokenLongExpiry();
90 }
margarethad7163122022-04-11 09:42:41 +020091 }
margaretha35e1ca22023-11-16 22:00:01 +010092 else if (type.equals(OAuth2ClientType.PUBLIC)) {
93 throw new KustvaktException(
94 StatusCodes.INVALID_REFRESH_TOKEN_EXPIRY,
margaretha7d7e25e2022-05-27 08:41:25 +020095 "Custom refresh token expiry is only applicable for confidential clients");
96 }
margaretha35e1ca22023-11-16 22:00:01 +010097 else if (refreshTokenExpiry > 31536000) {
98 throw new KustvaktException(
99 StatusCodes.INVALID_REFRESH_TOKEN_EXPIRY,
margaretha7d7e25e2022-05-27 08:41:25 +0200100 "Maximum refresh token expiry is 31536000 seconds (1 year)");
101 }
margaretha35e1ca22023-11-16 22:00:01 +0100102
margaretha977fabe2022-04-28 09:23:47 +0200103 client.setRefreshTokenExpiry(refreshTokenExpiry);
margaretha31a9f522018-04-03 20:40:45 +0200104 entityManager.persist(client);
105 }
106
margaretha0e8f4e72018-04-05 14:11:52 +0200107 public OAuth2Client retrieveClientById (String clientId)
margaretha05122312018-04-16 15:01:34 +0200108 throws KustvaktException {
margaretha0e8f4e72018-04-05 14:11:52 +0200109
margarethaa0486272018-04-12 19:59:31 +0200110 ParameterChecker.checkStringValue(clientId, "client_id");
margaretha05122312018-04-16 15:01:34 +0200111
margaretha0e8f4e72018-04-05 14:11:52 +0200112 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
margaretha35e1ca22023-11-16 22:00:01 +0100113 CriteriaQuery<OAuth2Client> query = builder
114 .createQuery(OAuth2Client.class);
margaretha0e8f4e72018-04-05 14:11:52 +0200115
116 Root<OAuth2Client> root = query.from(OAuth2Client.class);
117 query.select(root);
118 query.where(builder.equal(root.get(OAuth2Client_.id), clientId));
119
120 Query q = entityManager.createQuery(query);
margaretha05122312018-04-16 15:01:34 +0200121 try {
margaretha0e8f4e72018-04-05 14:11:52 +0200122 return (OAuth2Client) q.getSingleResult();
margaretha05122312018-04-16 15:01:34 +0200123 }
margarethaf839dde2018-04-16 17:52:57 +0200124 catch (NoResultException e) {
125 throw new KustvaktException(StatusCodes.CLIENT_NOT_FOUND,
margaretha7da23902022-05-02 08:38:45 +0200126 "Unknown client: " + clientId, "invalid_client");
margarethaf839dde2018-04-16 17:52:57 +0200127 }
margaretha05122312018-04-16 15:01:34 +0200128 catch (Exception e) {
129 throw new KustvaktException(StatusCodes.CLIENT_NOT_FOUND,
130 e.getMessage(), "invalid_client");
131 }
margaretha0e8f4e72018-04-05 14:11:52 +0200132 }
133
margaretha835178d2018-08-15 19:04:03 +0200134 public void deregisterClient (OAuth2Client client)
135 throws KustvaktException {
margaretha7f5071f2018-08-14 15:58:51 +0200136 ParameterChecker.checkObjectValue(client, "client");
margaretha8d804f62018-04-10 12:39:56 +0200137 if (!entityManager.contains(client)) {
138 client = entityManager.merge(client);
139 }
140 entityManager.remove(client);
141 }
margaretha31a9f522018-04-03 20:40:45 +0200142
margaretha7f5071f2018-08-14 15:58:51 +0200143 public void updateClient (OAuth2Client client) throws KustvaktException {
144 ParameterChecker.checkObjectValue(client, "client");
145 client = entityManager.merge(client);
146 }
147
margaretha7a09e482019-11-14 14:34:07 +0100148 public List<OAuth2Client> retrieveUserAuthorizedClients (String username)
margaretha230effb2018-11-29 17:28:18 +0100149 throws KustvaktException {
150 ParameterChecker.checkStringValue(username, "username");
151
152 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
margaretha35e1ca22023-11-16 22:00:01 +0100153 CriteriaQuery<OAuth2Client> query = builder
154 .createQuery(OAuth2Client.class);
margaretha230effb2018-11-29 17:28:18 +0100155
156 Root<OAuth2Client> client = query.from(OAuth2Client.class);
margaretha35e1ca22023-11-16 22:00:01 +0100157 Join<OAuth2Client, RefreshToken> refreshToken = client
158 .join(OAuth2Client_.refreshTokens);
margaretha230effb2018-11-29 17:28:18 +0100159 Predicate condition = builder.and(
160 builder.equal(refreshToken.get(RefreshToken_.userId), username),
161 builder.equal(refreshToken.get(RefreshToken_.isRevoked), false),
162 builder.greaterThan(
163 refreshToken
164 .<ZonedDateTime> get(RefreshToken_.expiryDate),
165 ZonedDateTime
166 .now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))));
167 query.select(client);
168 query.where(condition);
margaretha5a2c34e2018-11-29 19:35:13 +0100169 query.distinct(true);
margaretha230effb2018-11-29 17:28:18 +0100170 TypedQuery<OAuth2Client> q = entityManager.createQuery(query);
171 return q.getResultList();
172 }
173
margaretha0afd44a2020-02-05 10:49:21 +0100174 public List<OAuth2Client> retrieveClientsByAccessTokens (String username)
175 throws KustvaktException {
176 ParameterChecker.checkStringValue(username, "username");
177
178 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
margaretha35e1ca22023-11-16 22:00:01 +0100179 CriteriaQuery<OAuth2Client> query = builder
180 .createQuery(OAuth2Client.class);
margaretha0afd44a2020-02-05 10:49:21 +0100181
182 Root<OAuth2Client> client = query.from(OAuth2Client.class);
margaretha35e1ca22023-11-16 22:00:01 +0100183 Join<OAuth2Client, AccessToken> accessToken = client
184 .join(OAuth2Client_.accessTokens);
margaretha0afd44a2020-02-05 10:49:21 +0100185 Predicate condition = builder.and(
186 builder.equal(accessToken.get(AccessToken_.userId), username),
187 builder.equal(accessToken.get(AccessToken_.isRevoked), false),
188 builder.greaterThan(
189 accessToken
190 .<ZonedDateTime> get(AccessToken_.expiryDate),
191 ZonedDateTime
192 .now(ZoneId.of(Attributes.DEFAULT_TIME_ZONE))));
193 query.select(client);
194 query.where(condition);
195 query.distinct(true);
196 TypedQuery<OAuth2Client> q = entityManager.createQuery(query);
197 return q.getResultList();
198 }
199
margaretha7a09e482019-11-14 14:34:07 +0100200 public List<OAuth2Client> retrieveUserRegisteredClients (String username)
201 throws KustvaktException {
202 ParameterChecker.checkStringValue(username, "username");
203
204 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
margaretha35e1ca22023-11-16 22:00:01 +0100205 CriteriaQuery<OAuth2Client> query = builder
206 .createQuery(OAuth2Client.class);
margaretha7a09e482019-11-14 14:34:07 +0100207
208 Root<OAuth2Client> client = query.from(OAuth2Client.class);
209 query.select(client);
210 query.where(builder.equal(client.get(OAuth2Client_.registeredBy),
211 username));
212 query.distinct(true);
213 TypedQuery<OAuth2Client> q = entityManager.createQuery(query);
214 return q.getResultList();
215 }
216
margarethae20a2802022-04-21 12:37:38 +0200217 public List<OAuth2Client> retrievePlugins (boolean isPermittedOnly)
218 throws KustvaktException {
219 CriteriaBuilder builder = entityManager.getCriteriaBuilder();
margaretha35e1ca22023-11-16 22:00:01 +0100220 CriteriaQuery<OAuth2Client> query = builder
221 .createQuery(OAuth2Client.class);
margarethae20a2802022-04-21 12:37:38 +0200222
223 Root<OAuth2Client> client = query.from(OAuth2Client.class);
margaretha35e1ca22023-11-16 22:00:01 +0100224 Predicate restrictions = builder
225 .isNotNull(client.get(OAuth2Client_.SOURCE));
margarethae20a2802022-04-21 12:37:38 +0200226 if (isPermittedOnly) {
227 restrictions = builder.and(restrictions,
228 builder.isTrue(client.get(OAuth2Client_.IS_PERMITTED)));
229 }
230
231 query.select(client);
232 query.where(restrictions);
233 query.distinct(true);
234 TypedQuery<OAuth2Client> q = entityManager.createQuery(query);
235 return q.getResultList();
236 }
237
margaretha31a9f522018-04-03 20:40:45 +0200238}