33import com .auth0 .jwt .exceptions .JWTVerificationException ;
44import com .auth0 .jwt .interfaces .Claim ;
55import com .auth0 .jwt .interfaces .DecodedJWT ;
6+ import com .cronutils .utils .Preconditions ;
67import io .quarkus .scheduler .Scheduled ;
78import io .quarkus .scheduler .ScheduledExecution ;
8- import jakarta .annotation .PostConstruct ;
99import jakarta .enterprise .context .ApplicationScoped ;
1010import jakarta .inject .Inject ;
1111import jakarta .transaction .Transactional ;
12+ import jakarta .validation .constraints .NotNull ;
1213import org .cryptomator .hub .entities .Settings ;
1314import org .eclipse .microprofile .config .inject .ConfigProperty ;
1415import org .jboss .logging .Logger ;
2627@ ApplicationScoped
2728public class LicenseHolder {
2829
29- private static final int SELFHOSTED_NOLICENSE_SEATS = 5 ;
30- private static final int MANAGED_NOLICENSE_SEATS = 0 ;
30+ private static final Logger LOG = Logger .getLogger (LicenseHolder .class );
3131
3232 @ Inject
3333 @ ConfigProperty (name = "hub.managed-instance" , defaultValue = "false" )
@@ -46,49 +46,65 @@ public class LicenseHolder {
4646
4747 @ Inject
4848 RandomMinuteSleeper randomMinuteSleeper ;
49+
4950 @ Inject
5051 Settings .Repository settingsRepo ;
5152
52- private static final Logger LOG = Logger .getLogger (LicenseHolder .class );
5353 private DecodedJWT license ;
5454
5555 /**
56- * Loads the license from the database or from init props, if present
56+ * Makes sure a valid (but possibly expired) license exists.
57+ * <p>
58+ * Called during {@link org.cryptomator.hub.Main application startup}.
59+ *
60+ * @throws JWTVerificationException if the license is invalid
5761 */
58- @ PostConstruct
59- void init () {
62+ @ Transactional
63+ public void ensureLicenseExists () throws JWTVerificationException {
6064 var settings = settingsRepo .get ();
6165 if (settings .getLicenseKey () != null && settings .getHubId () != null ) {
62- validateOrResetExistingLicense (settings );
66+ validateExistingLicense (settings );
6367 } else if (initialLicenseToken .isPresent () && initialId .isPresent ()) {
6468 validateAndApplyInitLicense (settings , initialLicenseToken .get (), initialId .get ());
69+ } else {
70+ requestAnonTrialLicense ();
6571 }
6672 }
6773
68- @ Transactional
69- void validateOrResetExistingLicense (Settings settings ) {
74+ @ Transactional ( Transactional . TxType . MANDATORY )
75+ void validateExistingLicense (Settings settings ) throws JWTVerificationException {
7076 try {
7177 this .license = licenseValidator .validate (settings .getLicenseKey (), settings .getHubId ());
78+ LOG .info ("Verified existing license." );
7279 } catch (JWTVerificationException e ) {
7380 LOG .warn ("License in database is invalid or does not match hubId" , e );
7481 LOG .warn ("Deleting license entry. Please add the license over the REST API again." );
75- settings .setLicenseKey (null );
76- settingsRepo .persistAndFlush (settings );
82+ throw e ;
7783 }
7884 }
7985
80- @ Transactional
81- void validateAndApplyInitLicense (Settings settings , String initialLicenseToken , String initialHubId ) {
86+ @ Transactional ( Transactional . TxType . MANDATORY )
87+ void validateAndApplyInitLicense (Settings settings , String initialLicenseToken , String initialHubId ) throws JWTVerificationException {
8288 try {
8389 this .license = licenseValidator .validate (initialLicenseToken , initialHubId );
8490 settings .setLicenseKey (initialLicenseToken );
8591 settings .setHubId (initialHubId );
8692 settingsRepo .persistAndFlush (settings );
93+ LOG .info ("Successfully imported license from property hub.initial-license." );
8794 } catch (JWTVerificationException e ) {
8895 LOG .warn ("Provided initial license is invalid or does not match inital hubId." , e );
96+ throw e ;
8997 }
9098 }
9199
100+
101+ @ Transactional (Transactional .TxType .MANDATORY )
102+ void requestAnonTrialLicense () {
103+ LOG .info ("No license found. Requesting trial license..." );
104+ // TODO
105+ throw new UnsupportedOperationException ("Not yet implemented" );
106+ }
107+
92108 /**
93109 * Parses, verifies and persists the given token as the license in the database.
94110 *
@@ -106,7 +122,7 @@ public void set(String token) throws JWTVerificationException {
106122 /**
107123 * Attempts to refresh the Hub licence every day between 01:00:00 and 02:00:00 AM UTC if claim refreshURL is present.
108124 */
109- @ Scheduled (cron = "0 0 1 * * ?" , timeZone = "UTC" , concurrentExecution = Scheduled .ConcurrentExecution .SKIP , skipExecutionIf = LicenseHolder . LicenseRefreshSkipper . class )
125+ @ Scheduled (cron = "0 0 1 * * ?" , timeZone = "UTC" , concurrentExecution = Scheduled .ConcurrentExecution .SKIP )
110126 void refreshLicense () throws InterruptedException {
111127 randomMinuteSleeper .sleep (); // add random sleep between [0,59]min to reduce infrastructure load
112128 var refreshUrlClaim = get ().getClaim ("refreshUrl" );
@@ -115,12 +131,12 @@ void refreshLicense() throws InterruptedException {
115131 var refreshUrl = URI .create (refreshUrlClaim .asString ());
116132 var refreshedLicense = requestLicenseRefresh (refreshUrl , get ().getToken ());
117133 set (refreshedLicense );
118- } catch (LicenseRefreshFailedException lrfe ) {
119- LOG .errorv ("Failed to refresh license token. Request to {0} was answerd with response code {1,number,integer}" , refreshUrlClaim , lrfe .statusCode );
134+ } catch (LicenseRefreshFailedException e ) {
135+ LOG .errorv ("Failed to refresh license token. Request to {0} was answerd with response code {1,number,integer}" , refreshUrlClaim , e .statusCode );
120136 } catch (IllegalArgumentException | IOException e ) {
121137 LOG .error ("Failed to refresh license token" , e );
122- } catch (JWTVerificationException jve ) {
123- LOG .error ("Failed to refresh license token. Refreshed token is invalid." , jve );
138+ } catch (JWTVerificationException e ) {
139+ LOG .error ("Failed to refresh license token. Refreshed token is invalid." , e );
124140 }
125141 }
126142 }
@@ -144,49 +160,37 @@ String requestLicenseRefresh(URI refreshUrl, String licenseToken) throws Interru
144160 }
145161 }
146162
163+ @ NotNull
147164 public DecodedJWT get () {
148- return license ;
165+ return Preconditions . checkNotNull ( license ) ;
149166 }
150167
151168 /**
152169 * Checks if the license is set.
153170 *
154171 * @return {@code true}, if the license _is not null_. Otherwise false.
155172 */
173+ @ Deprecated // FIXME remove this method!
156174 public boolean isSet () {
157175 return license != null ;
158176 }
159177
160178 /**
161179 * Checks if the license is expired.
162180 *
163- * @return {@code true}, if the license _is not nul and expired_. Otherwise false.
181+ * @return {@code true}, if the license expired, {@code false} otherwise .
164182 */
165183 public boolean isExpired () {
166- return Optional .ofNullable (license ) //
167- .map (l -> l .getExpiresAt ().toInstant ().isBefore (Instant .now ())) //
168- .orElse (false );
184+ return Preconditions .checkNotNull (license ).getExpiresAt ().toInstant ().isBefore (Instant .now ());
169185 }
170186
171187 /**
172188 * Gets the number of seats in the license
173189 *
174- * @return Number of seats of the license, if license is not null. Otherwise {@value SELFHOSTED_NOLICENSE_SEATS}.
190+ * @return Number of seats of the license
175191 */
176192 public long getSeats () {
177- return Optional .ofNullable (license ) //
178- .map (l -> l .getClaim ("seats" )) //
179- .map (Claim ::asLong ) //
180- .orElseGet (this ::seatsOnNotExisingLicense );
181- }
182-
183- //visible for testing
184- public long seatsOnNotExisingLicense () {
185- if (!managedInstance ) {
186- return SELFHOSTED_NOLICENSE_SEATS ;
187- } else {
188- return MANAGED_NOLICENSE_SEATS ;
189- }
193+ return Preconditions .checkNotNull (license ).getClaim ("seats" ).asLong ();
190194 }
191195
192196 public boolean isManagedInstance () {
@@ -203,15 +207,4 @@ static class LicenseRefreshFailedException extends RuntimeException {
203207 }
204208 }
205209
206- @ ApplicationScoped
207- public static class LicenseRefreshSkipper implements Scheduled .SkipPredicate {
208-
209- @ Inject
210- LicenseHolder licenseHolder ;
211-
212- @ Override
213- public boolean test (ScheduledExecution execution ) {
214- return licenseHolder .license == null ;
215- }
216- }
217210}
0 commit comments