blob: 31a8954a6eca6fa4dc52cb581478599dbc668ce0 [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.knox.gateway.services.token.impl.state;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.services.token.state.JournalEntry;
import org.apache.knox.gateway.services.token.state.TokenStateJournal;
import org.apache.knox.gateway.services.token.impl.TokenStateServiceMessages;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
/**
* Base class for TokenStateJournal implementations that employ files for persistence.
*/
abstract class FileTokenStateJournal implements TokenStateJournal {
protected static final int INDEX_TOKEN_ID = 0;
protected static final int INDEX_ISSUE_TIME = 1;
protected static final int INDEX_EXPIRATION = 2;
protected static final int INDEX_MAX_LIFETIME = 3;
protected static final TokenStateServiceMessages log = MessagesFactory.get(TokenStateServiceMessages.class);
// The name of the journal directory
protected static final String JOURNAL_DIR_NAME = "token-state";
/**
* The journal directory path
*/
protected final Path journalDir;
protected FileTokenStateJournal(GatewayConfig config) throws IOException {
journalDir = Paths.get(config.getGatewaySecurityDir(), JOURNAL_DIR_NAME);
if (!Files.exists(journalDir)) {
Files.createDirectories(journalDir);
}
}
@Override
public abstract void add(String tokenId, long issueTime, long expiration, long maxLifetime) throws IOException;
@Override
public void add(JournalEntry entry) throws IOException {
add(Collections.singletonList(entry));
}
@Override
public abstract void add(List<JournalEntry> entries) throws IOException;
@Override
public List<JournalEntry> get() throws IOException {
return loadJournal();
}
@Override
public abstract JournalEntry get(String tokenId) throws IOException;
@Override
public void remove(final String tokenId) throws IOException {
remove(Collections.singleton(tokenId));
}
@Override
public abstract void remove(Collection<String> tokenIds) throws IOException;
@Override
public void remove(final JournalEntry entry) throws IOException {
remove(entry.getTokenId());
}
protected abstract List<JournalEntry> loadJournal() throws IOException;
protected List<FileJournalEntry> loadJournal(FileChannel channel) throws IOException {
List<FileJournalEntry> entries = new ArrayList<>();
try (InputStream input = Channels.newInputStream(channel)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
String line;
while ((line = reader.readLine()) != null) {
entries.add(FileJournalEntry.parse(line));
}
}
return entries;
}
/**
* Parse the String representation of an entry.
*
* @param entry A journal file entry line
*
* @return A FileJournalEntry object created from the specified entry.
*/
protected FileJournalEntry parse(final String entry) {
return FileJournalEntry.parse(entry);
}
/**
* A JournalEntry implementation for File-based TokenStateJournal implementations
*/
static final class FileJournalEntry implements JournalEntry {
private final String tokenId;
private final String issueTime;
private final String expiration;
private final String maxLifetime;
FileJournalEntry(final String tokenId, long issueTime, long expiration, long maxLifetime) {
this(tokenId, String.valueOf(issueTime), String.valueOf(expiration), String.valueOf(maxLifetime));
}
FileJournalEntry(final String tokenId,
final String issueTime,
final String expiration,
final String maxLifetime) {
this.tokenId = tokenId;
this.issueTime = issueTime;
this.expiration = expiration;
this.maxLifetime = maxLifetime;
}
@Override
public String getTokenId() {
return tokenId;
}
@Override
public String getIssueTime() {
return issueTime;
}
@Override
public String getExpiration() {
return expiration;
}
@Override
public String getMaxLifetime() {
return maxLifetime;
}
@Override
public String toString() {
String[] elements = new String[4];
elements[INDEX_TOKEN_ID] = getTokenId();
String issueTime = getIssueTime();
elements[INDEX_ISSUE_TIME] = (issueTime != null) ? issueTime : "";
String expiration = getExpiration();
elements[INDEX_EXPIRATION] = (expiration != null) ? expiration : "";
String maxLifetime = getMaxLifetime();
elements[INDEX_MAX_LIFETIME] = (maxLifetime != null) ? maxLifetime : "";
return String.format(Locale.ROOT,
"%s,%s,%s,%s",
elements[INDEX_TOKEN_ID],
elements[INDEX_ISSUE_TIME],
elements[INDEX_EXPIRATION],
elements[INDEX_MAX_LIFETIME]);
}
/**
* Parse the String representation of an entry.
*
* @param entry A journal file entry line
*
* @return A FileJournalEntry object created from the specified entry.
*/
static FileJournalEntry parse(final String entry) {
String[] elements = entry.split(",");
if (elements.length < 4) {
throw new IllegalArgumentException("Invalid journal entry: " + entry);
}
String tokenId = elements[INDEX_TOKEN_ID].trim();
String issueTime = elements[INDEX_ISSUE_TIME].trim();
String expiration = elements[INDEX_EXPIRATION].trim();
String maxLifetime = elements[INDEX_MAX_LIFETIME].trim();
return new FileJournalEntry(tokenId.isEmpty() ? null : tokenId,
issueTime.isEmpty() ? null : issueTime,
expiration.isEmpty() ? null : expiration,
maxLifetime.isEmpty() ? null : maxLifetime);
}
}
}