blob: 308c221c2a8ef7f154ab4837f55ada2c8751cd08 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rj.server.srvext.auth;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.rj.RjException;
import org.eclipse.statet.rj.server.FxCallback;
import org.eclipse.statet.rj.server.srvext.ServerAuthMethod;
import org.eclipse.statet.rj.server.util.ServerUtils;
import org.eclipse.statet.rj.server.util.ServerUtils.ArgKeyValue;
/**
* Authentication method 'none'
* without any authentication mechanism.
*/
@NonNullByDefault
public class FxAuthMethod extends ServerAuthMethod {
private final byte[] pendingKey= new byte[1024];
private File file= nonNullLateInit();
private FileChannel fileChannel= nonNullLateInit();
public FxAuthMethod() {
super("fx", false);
}
@Override
public void doInit(final @Nullable String arg) throws RjException {
final ArgKeyValue config= ServerUtils.getArgConfigValue(arg);
if (config.getKey().equals("file")) {
final String fileName= config.getValue();
if (fileName == null || fileName.isEmpty()) {
throw new RjException("Missing lock file name.", null);
}
this.file= new File(fileName);
try {
if (!this.file.exists()) {
this.file.createNewFile();
}
this.fileChannel= new RandomAccessFile(this.file, "rws").getChannel();
this.fileChannel.truncate(512);
}
catch (final IOException e) {
throw new RjException("Cannot read lock file.", e);
}
}
else {
throw new RjException(String.format("Unsupported configuration type '%1$s'.", config.getKey()));
}
}
@Override
protected ImList<Callback> doCreateLogin() throws RjException {
getRandom().nextBytes(this.pendingKey);
try {
this.fileChannel.position(this.fileChannel.size());
}
catch (final IOException e) {
throw new RjException("Cannot read lock file.", e);
}
return ImCollections.newList(
new NameCallback("Username"),
new FxCallback(this.file.getPath(), this.pendingKey) );
}
@Override
protected String doPerformLogin(final ImList<Callback> callbacks) throws LoginException, RjException {
final String userName= ((NameCallback)callbacks.get(0)).getName();
if (userName == null) {
throw new RjException("Username is missing.");
}
final byte[] clientKey= ((FxCallback)callbacks.get(1)).getContent();
if (clientKey.length < 1024) {
throw new RjException("Unsufficient client key");
}
try {
if (compare(this.pendingKey) && compare(clientKey)) {
return userName;
}
}
catch (final IOException e) {
throw new RjException("Cannot read lock file.", e);
}
throw new FailedLoginException();
}
private boolean compare(final byte[] key) throws IOException {
final byte[] check= new byte[key.length];
final int n= this.fileChannel.read(ByteBuffer.wrap(check));
if (n != key.length) {
return false;
}
return Arrays.equals(key, check);
}
}