1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package fr.cnes.doi.security;
20
21 import fr.cnes.doi.db.AbstractTokenDBHelper;
22 import fr.cnes.doi.db.model.DOIProject;
23 import fr.cnes.doi.exception.DOIDbException;
24 import fr.cnes.doi.exception.DoiRuntimeException;
25 import fr.cnes.doi.exception.TokenSecurityException;
26 import fr.cnes.doi.plugin.PluginFactory;
27 import fr.cnes.doi.settings.Consts;
28 import fr.cnes.doi.settings.DoiSettings;
29 import fr.cnes.doi.utils.UniqueProjectName;
30 import fr.cnes.doi.utils.spec.Requirement;
31 import io.jsonwebtoken.Claims;
32 import io.jsonwebtoken.ExpiredJwtException;
33 import io.jsonwebtoken.Jws;
34 import io.jsonwebtoken.Jwts;
35 import io.jsonwebtoken.MalformedJwtException;
36 import io.jsonwebtoken.SignatureAlgorithm;
37 import io.jsonwebtoken.SignatureException;
38 import io.jsonwebtoken.UnsupportedJwtException;
39 import io.jsonwebtoken.impl.TextCodec;
40 import io.jsonwebtoken.impl.crypto.MacProvider;
41 import java.security.Key;
42 import java.time.Instant;
43 import java.util.ArrayList;
44 import java.util.Calendar;
45 import java.util.Date;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.stream.Collectors;
49 import org.apache.logging.log4j.LogManager;
50 import org.apache.logging.log4j.Logger;
51 import org.restlet.data.Status;
52
53
54
55
56
57
58 @Requirement(reqId = Requirement.DOI_AUTH_020, reqName = Requirement.DOI_AUTH_020_NAME)
59 public final class TokenSecurity {
60
61
62
63
64 private String tokenKey;
65
66
67
68
69 public static final String PROJECT_ID = "projectID";
70
71
72
73
74 public static final String PROJECT_NAME = "projectName";
75
76
77
78
79 public static final String DEFAULT_TOKEN_KEY = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=";
80
81
82
83
84 public static final String DATE_FORMAT = "EEE MMM dd HH:mm:ss z yyyy";
85
86
87
88
89 private static final AbstractTokenDBHelper TOKEN_DB = PluginFactory.getToken();
90
91
92
93
94 private static final Logger LOG = LogManager.getLogger(TokenSecurity.class.getName());
95
96
97
98
99
100
101 public static TokenSecurity getInstance() {
102 LOG.traceEntry();
103 return LOG.traceExit(TokenSecurityHolder.INSTANCE);
104 }
105
106
107
108
109
110
111
112 public static String createKeySignatureHS256() {
113 LOG.traceEntry();
114 final Key key = MacProvider.generateKey(SignatureAlgorithm.HS256);
115 return LOG.traceExit(TextCodec.BASE64.encode(key.getEncoded()));
116 }
117
118
119
120
121 private TokenSecurity() {
122 LOG.traceEntry();
123 init();
124 LOG.traceExit();
125 }
126
127
128
129
130 private void init() {
131 LOG.traceEntry();
132 final String token = DoiSettings.getInstance().getString(Consts.TOKEN_KEY);
133 this.tokenKey = (token == null) ? DEFAULT_TOKEN_KEY : token;
134 LOG.traceExit();
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148 public String generate(final String userID,
149 final int projectID,
150 final TokenSecurity.TimeUnit timeUnit,
151 final int amount) throws TokenSecurityException {
152 LOG.traceEntry("Parameters : {}, {}, {} and {}", userID, projectID, timeUnit, amount);
153 List<DOIProject> projects;
154 try {
155 projects = UniqueProjectName.getInstance().getProjects();
156 } catch (DOIDbException ex) {
157 projects = new ArrayList<>();
158 }
159 final Map<Integer, String> result = projects.stream().collect(
160 Collectors.toMap(DOIProject::getSuffix, DOIProject::getProjectname));
161 final String projectName = result.get(projectID);
162 if (projectName.isEmpty()) {
163 throw LOG.throwing(new TokenSecurityException(
164 Status.CLIENT_ERROR_BAD_REQUEST,
165 "No register " + PROJECT_ID + ", please create one")
166 );
167 }
168 final Date now = Date.from(Instant.now());
169 final Date expirationTime = computeExpirationDate(now, timeUnit.getTimeUnit(), amount);
170
171 final String token = Jwts.builder()
172 .setIssuer(DoiSettings.getInstance().getString(Consts.APP_NAME))
173 .setIssuedAt(Date.from(Instant.now()))
174 .setSubject(userID)
175 .claim(PROJECT_ID, projectID)
176 .claim(PROJECT_NAME, projectName)
177 .setExpiration(expirationTime)
178 .signWith(
179 SignatureAlgorithm.HS256,
180 TextCodec.BASE64.decode(getTokenKey())
181 )
182 .compact();
183 LOG.debug(String.format("token generated : %s", token));
184 return LOG.traceExit(token);
185 }
186
187
188
189
190
191
192
193
194
195 public String generate(final String userID,
196 final TokenSecurity.TimeUnit timeUnit,
197 final int amount) {
198 LOG.traceEntry("Parameters : {}, {} and {}", userID, timeUnit, amount);
199
200 final Date now = Date.from(Instant.now());
201 final Date expirationTime = computeExpirationDate(now, timeUnit.getTimeUnit(), amount);
202
203 final String token = Jwts.builder()
204 .setIssuer(DoiSettings.getInstance().getString(Consts.APP_NAME))
205 .setIssuedAt(Date.from(Instant.now()))
206 .setSubject(userID)
207 .setExpiration(expirationTime)
208 .signWith(
209 SignatureAlgorithm.HS256,
210 TextCodec.BASE64.decode(getTokenKey())
211 )
212 .compact();
213 LOG.debug(String.format("token generated : %s", token));
214 return LOG.traceExit(token);
215 }
216
217
218
219
220
221
222 public String getTokenKey() {
223 LOG.traceEntry();
224 return LOG.traceExit(this.tokenKey);
225 }
226
227
228
229
230
231
232 public void setTokenKey(final String tokenKey) {
233 LOG.traceEntry("Parameter : {}", tokenKey);
234 this.tokenKey = tokenKey;
235 LOG.debug(String.format("Set tokenKey to %s", tokenKey));
236 LOG.traceExit();
237 }
238
239
240
241
242
243
244
245 public boolean isExpired(final String token) {
246 LOG.traceEntry("Parameter\n\ttoken: {}", token);
247 final Jws<Claims> jws = this.getTokenInformation(token);
248 return LOG.traceExit(jws == null);
249 }
250
251
252
253
254
255
256
257
258
259 public Jws<Claims> getTokenInformation(final String jwtToken) throws DoiRuntimeException {
260 LOG.traceEntry("Parameter : {}", jwtToken);
261 Jws<Claims> token;
262 try {
263 token = Jwts.parser()
264 .requireIssuer(DoiSettings.getInstance().getString(Consts.APP_NAME))
265 .setSigningKey(TextCodec.BASE64.decode(getTokenKey()))
266 .parseClaimsJws(jwtToken);
267 } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException ex) {
268 throw LOG.throwing(new DoiRuntimeException("Unable to get the token information", ex));
269 } catch (ExpiredJwtException e) {
270 LOG.info("Cannot get the token information", e);
271 getTokenDB().deleteToken(jwtToken);
272 token = null;
273 }
274 return LOG.traceExit(token);
275 }
276
277
278
279
280
281
282 public AbstractTokenDBHelper getTokenDB() {
283 LOG.traceEntry();
284 return LOG.traceExit(TokenSecurity.TOKEN_DB);
285 }
286
287
288
289
290
291
292
293
294
295 private Date computeExpirationDate(final Date now,
296 final int calendarTime,
297 final int amount) {
298 LOG.traceEntry("Parameters : {}, {} and {}", now, calendarTime, amount);
299 final Calendar calendar = Calendar.getInstance();
300 calendar.setTime(now);
301 calendar.add(calendarTime, amount);
302 return LOG.traceExit(calendar.getTime());
303 }
304
305
306
307
308
309 private static class TokenSecurityHolder {
310
311
312
313
314 private static final TokenSecurity INSTANCE = new TokenSecurity();
315 }
316
317
318
319
320 public enum TimeUnit {
321
322
323
324 HOUR(Calendar.HOUR),
325
326
327
328 DAY(Calendar.DATE),
329
330
331
332 YEAR(Calendar.YEAR);
333
334
335
336
337 private final int timeUnit;
338
339
340
341
342
343
344 TimeUnit(final int timeUnit) {
345 this.timeUnit = timeUnit;
346 }
347
348
349
350
351
352
353 public int getTimeUnit() {
354 return this.timeUnit;
355 }
356
357
358
359
360
361
362
363 public static TimeUnit getTimeUnitFrom(final int value) {
364 TimeUnit result = null;
365 final TimeUnit[] units = TimeUnit.values();
366 for (final TimeUnit unit : units) {
367 if (unit.getTimeUnit() == value) {
368 result = unit;
369 break;
370 }
371 }
372 return result;
373 }
374 }
375 }