/* dCache - http://www.dcache.org/
*
* Copyright (C) 2014 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.gridsite;
import eu.emi.security.authn.x509.X509Credential;
import org.springframework.beans.factory.annotation.Required;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Stream;
import org.dcache.auth.FQAN;
import org.dcache.delegation.gridsite2.DelegationException;
import org.dcache.srm.request.RequestCredential;
import org.dcache.srm.request.RequestCredentialStorage;
import org.dcache.util.SqlGlob;
import static org.dcache.gridsite.Utilities.assertThat;
/**
* The SrmCredentialStore acts as a bridge between the SRM's delegation store
* and the API expected by GridSite.
*/
public class SrmCredentialStore implements CredentialStore
{
private RequestCredentialStorage _store;
@Required
public void setRequestCredentialStorage(RequestCredentialStorage store)
{
_store = store;
}
@Override
public X509Credential get(DelegationIdentity id) throws DelegationException
{
RequestCredential credential =
_store.getRequestCredential(nameFromId(id), null);
assertThat(credential != null, "no stored credential", id);
return credential.getDelegatedCredential();
}
@Override
public void put(DelegationIdentity id, X509Credential credential, FQAN primaryFqan)
throws DelegationException
{
try {
RequestCredential srmCredential =
new RequestCredential(nameFromId(id), Objects.toString(primaryFqan, null), credential, _store);
_store.saveRequestCredential(srmCredential);
} catch (RuntimeException e) {
throw new DelegationException("failed to save credential: " + e.getMessage());
}
}
@Override
public void remove(DelegationIdentity id) throws DelegationException
{
boolean isSuccessful;
try {
isSuccessful = _store.deleteRequestCredential(nameFromId(id), null);
} catch (IOException e) {
throw new DelegationException("internal problem: " + e.getMessage());
}
assertThat(isSuccessful, "no credential", id);
}
@Override
public boolean has(DelegationIdentity id) throws DelegationException
{
try {
return _store.hasRequestCredential(nameFromId(id), null);
} catch (IOException e) {
throw new DelegationException("internal problem: " + e.getMessage());
}
}
@Override
public Calendar getExpiry(DelegationIdentity id) throws DelegationException
{
RequestCredential credential =
_store.getRequestCredential(nameFromId(id), null);
assertThat(credential != null, "no credential", id);
Date expiry = new Date(credential.getDelegatedCredentialExpiration());
Calendar result = Calendar.getInstance();
result.setTime(expiry);
return result;
}
private static String nameFromId(DelegationIdentity id)
{
// Treat the delegation ID 'gsi' as a special case that maps to
// the storage for this user via GSI.
if (id.getDelegationId().equals("gsi")) {
return id.getDn();
} else {
return id.getDelegationId() + " " + id.getDn();
}
}
@Override
public X509Credential search(String dn)
{
X509Credential bestWithFqan = search(dn, new SqlGlob("*"));
X509Credential bestWithoutFqan = search(dn, (SqlGlob)null);
if (bestWithFqan == null) {
return bestWithoutFqan;
} else if (bestWithoutFqan == null) {
return bestWithFqan;
}
Date bestWithFqanLifetime = expiryDateFor(bestWithFqan);
Date bestWithoutFqanLifetime = expiryDateFor(bestWithoutFqan);
return bestWithoutFqanLifetime.after(bestWithFqanLifetime) ? bestWithoutFqan : bestWithFqan;
}
private static Date expiryDateFor(X509Credential credential)
{
return Stream.of(credential.getCertificateChain())
.map(X509Certificate::getNotAfter)
.min(Date::compareTo)
.orElseThrow(() -> new IllegalArgumentException("Certificate chain is empty."));
}
@Override
public X509Credential search(String dn, String fqan)
{
return search(dn, fqan != null ? new SqlGlob(fqan) : null);
}
private X509Credential search(String dn, SqlGlob fqan)
{
long lifetime = 0;
RequestCredential credential = null;
RequestCredential gsiCredential = _store.searchRequestCredential(new SqlGlob(dn), fqan);
if (gsiCredential != null) {
lifetime = gsiCredential.getDelegatedCredentialRemainingLifetime();
if (lifetime > 0) {
credential = gsiCredential;
}
}
RequestCredential gridsiteCredential = _store.searchRequestCredential(new SqlGlob("* " + dn), fqan);
if (gridsiteCredential != null &&
gridsiteCredential.getDelegatedCredentialRemainingLifetime() > lifetime) {
credential = gridsiteCredential;
}
return credential != null ? credential.getDelegatedCredential() : null;
}
}