blob: 12c555233bf1d1bb544c336aac63dda4ae897d6d [file] [log] [blame]
// Copyright (C) 2017 The Android Open Source Project
//
// 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.googlesource.gerrit.plugins.lfs;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
class LfsSshRequestAuthorizer {
static class SshAuthInfo extends AuthInfo {
SshAuthInfo(String authToken, Instant issued, Long expiresIn) {
super(SSH_AUTH_PREFIX + authToken, issued, expiresIn);
}
}
private static final Logger log = LoggerFactory.getLogger(LfsSshRequestAuthorizer.class);
private static final int DEFAULT_SSH_TIMEOUT = 10;
static final String SSH_AUTH_PREFIX = "Ssh: ";
private final Processor processor;
private final Long expiresIn;
@Inject
LfsSshRequestAuthorizer(Processor processor, LfsConfigurationFactory configFactory) {
this.processor = processor;
long timeout = DEFAULT_SSH_TIMEOUT;
try {
timeout =
configFactory
.getGlobalConfig()
.getInt("auth", null, "sshExpirationSeconds", DEFAULT_SSH_TIMEOUT);
} catch (IllegalArgumentException e) {
log.warn(
"Reading expiration timeout failed with error." + " Falling back to default {}",
DEFAULT_SSH_TIMEOUT,
e);
}
this.expiresIn = timeout;
}
SshAuthInfo generateAuthInfo(CurrentUser user, String project, String operation) {
LfsSshAuthToken token =
new LfsSshAuthToken(user.getUserName(), project, operation, Instant.now(), expiresIn);
return new SshAuthInfo(processor.serialize(token), token.issued, token.expiresIn);
}
Optional<String> getUserFromValidToken(String authToken, String project, String operation) {
Optional<LfsSshAuthToken> token = processor.deserialize(authToken);
if (!token.isPresent()) {
return Optional.empty();
}
Verifier verifier = new Verifier(token.get(), project, operation);
if (!verifier.verify()) {
log.error("Invalid data was provided with auth token {}.", authToken);
return Optional.empty();
}
return Optional.of(token.get().user);
}
static class Processor extends LfsAuthToken.Processor<LfsSshAuthToken> {
@Inject
protected Processor(LfsCipher cipher) {
super(cipher);
}
@Override
protected List<String> getValues(LfsSshAuthToken token) {
List<String> values = new ArrayList<>(4);
values.add(token.user);
values.add(token.project);
values.add(token.operation);
values.add(LfsDateTime.format(token.issued));
values.add(String.valueOf(token.expiresIn));
return values;
}
@Override
protected Optional<LfsSshAuthToken> createToken(List<String> values) {
if (values.size() != 5) {
return Optional.empty();
}
return Optional.of(
new LfsSshAuthToken(
values.get(0),
values.get(1),
values.get(2),
values.get(3),
Long.valueOf(values.get(4))));
}
}
private static class Verifier extends LfsAuthToken.Verifier<LfsSshAuthToken> {
private final String project;
private final String operation;
protected Verifier(LfsSshAuthToken token, String project, String operation) {
super(token);
this.project = project;
this.operation = operation;
}
@Override
protected boolean verifyTokenValues() {
return project.equals(token.project) && operation.equals(token.operation);
}
}
private static class LfsSshAuthToken extends LfsAuthToken {
private final String user;
private final String project;
private final String operation;
LfsSshAuthToken(String user, String project, String operation, Instant issued, Long expiresIn) {
super(issued, expiresIn);
this.user = user;
this.project = project;
this.operation = operation;
}
LfsSshAuthToken(String user, String project, String operation, String issued, Long expiresIn) {
super(issued, expiresIn);
this.user = user;
this.project = project;
this.operation = operation;
}
}
}