blob: d62ffe218b0ae556cd80a26e09750eeed25d8737 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.syncope.wa.starter.webauthn;
import com.fasterxml.jackson.core.type.TypeReference;
import com.yubico.data.CredentialRegistration;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.wa.WebAuthnAccount;
import org.apache.syncope.common.lib.wa.WebAuthnDeviceCredential;
import org.apache.syncope.common.rest.api.service.wa.WebAuthnRegistrationService;
import org.apache.syncope.wa.bootstrap.WARestClient;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.util.crypto.CipherExecutor;
import org.apereo.cas.webauthn.WebAuthnUtils;
import org.apereo.cas.webauthn.storage.BaseWebAuthnCredentialRepository;
import org.jooq.lambda.Unchecked;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class WAWebAuthnCredentialRepository extends BaseWebAuthnCredentialRepository {
private static final Logger LOG = LoggerFactory.getLogger(WAWebAuthnCredentialRepository.class);
private final WARestClient waRestClient;
public WAWebAuthnCredentialRepository(final CasConfigurationProperties properties,
final WARestClient waRestClient) {
super(properties, CipherExecutor.noOpOfStringToString());
this.waRestClient = waRestClient;
}
@Override
public boolean removeRegistrationByUsername(final String username,
final CredentialRegistration credentialRegistration) {
String id = credentialRegistration.getCredential().getCredentialId().getHex();
getService().delete(username, id);
return true;
}
@Override
public boolean removeAllRegistrations(final String username) {
getService().delete(username);
return true;
}
@Override
public Stream<? extends CredentialRegistration> stream() {
return getService().list().
stream().
map(WebAuthnAccount::getCredentials).
flatMap(Collection::stream).
map(Unchecked.function(record -> {
String json = getCipherExecutor().decode(record.getJson());
return WebAuthnUtils.getObjectMapper().readValue(json, new TypeReference<>() {
});
}));
}
@Override
protected void update(final String username, final Collection<CredentialRegistration> records) {
try {
List<WebAuthnDeviceCredential> credentials = records.stream().
map(Unchecked.function(record -> {
String json = getCipherExecutor().encode(WebAuthnUtils.getObjectMapper().
writeValueAsString(record));
return new WebAuthnDeviceCredential.Builder().
json(json).
identifier(record.getCredential().getCredentialId().getHex()).
build();
})).
collect(Collectors.toList());
WebAuthnAccount account = getService().read(username);
if (account != null) {
account.getCredentials().addAll(credentials);
getService().update(username, account);
} else {
account = new WebAuthnAccount.Builder().credentials(credentials).build();
getService().create(username, account);
}
} catch (final Exception e) {
LOG.error(e.getMessage(), e);
}
}
@Override
public Collection<CredentialRegistration> getRegistrationsByUsername(final String username) {
try {
return getService().read(username).getCredentials().stream().
map(Unchecked.function(record -> {
String json = getCipherExecutor().decode(record.getJson());
return WebAuthnUtils.getObjectMapper()
.readValue(json, new TypeReference<CredentialRegistration>() {
});
})).
collect(Collectors.toList());
} catch (final SyncopeClientException e) {
if (e.getType() == ClientExceptionType.NotFound) {
LOG.info("Could not locate account for {}", username);
} else {
LOG.error(e.getMessage(), e);
}
} catch (final Exception e) {
LOG.error(e.getMessage(), e);
}
return List.of();
}
private WebAuthnRegistrationService getService() {
if (!WARestClient.isReady()) {
throw new IllegalStateException("Syncope core is not yet ready");
}
return waRestClient.getSyncopeClient().getService(WebAuthnRegistrationService.class);
}
}