/** * Copyright © 2013 enioka. All rights reserved * * 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 com.enioka.jqm.api; import java.util.Calendar; import java.util.UUID; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.crypto.hash.Sha512Hash; import org.apache.shiro.util.ByteSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.enioka.jqm.jdbc.DbConn; import com.enioka.jqm.model.GlobalParameter; import com.enioka.jqm.model.RUser; /** * Helper class helping dealing with internal security when calling the simple REST API (the API dealing with file transfers)<br> * This API may be secured - therefore, we must create a temporary internal user with the necessary permissions. * */ final class SimpleApiSecurity { private static Logger jqmlogger = LoggerFactory.getLogger(SimpleApiSecurity.class); private static volatile RUser user; private static volatile String secret; private static volatile Duet logindata; private static Boolean useAuth = null; private static volatile Object lock = new Object(); static class Duet { String usr; String pass; } private SimpleApiSecurity() { // Helper class } /** * Will create (or recreate) if necessary the temporary login data.<br> * Will create its own transaction - therefore the given connection must not have any active transaction. */ static Duet getId(DbConn cnx) { if (logindata == null && useAuth == null) { useAuth = Boolean.parseBoolean(GlobalParameter.getParameter(cnx, "enableWsApiAuth", "true")); if (!useAuth) { jqmlogger.debug("The client API will not use any authentication to download files"); logindata = new Duet(); logindata.pass = null; logindata.usr = null; } else { jqmlogger.debug("The client API will use authentication to download files"); } } if (!useAuth) { return logindata; } if (user == null || user.getExpirationDate().before(Calendar.getInstance())) { synchronized (lock) { if (user == null || user.getExpirationDate().before(Calendar.getInstance())) { jqmlogger.debug("The client API will create an internal secret to access the simple API for file downloading"); // Create new String login = UUID.randomUUID().toString(); secret = UUID.randomUUID().toString(); ByteSource salt = new SecureRandomNumberGenerator().nextBytes(); String hash = new Sha512Hash(secret, salt, 100000).toHex(); String saltS = salt.toHex(); Calendar expiration = Calendar.getInstance(); expiration.add(Calendar.DAY_OF_YEAR, 1); int id = RUser.create(cnx, login, hash, saltS, expiration, true, "administrator"); user = RUser.select_id(cnx, id); logindata = new Duet(); logindata.pass = secret; logindata.usr = login; // Purge all old internal accounts cnx.runUpdate("user_delete_expired_internal"); cnx.commit(); } } } return logindata; } static void dispose() { user = null; logindata = null; secret = null; useAuth = null; } }