blob: f0414a2c1ffcff471f968f4152fb44a0cd1738a0 [file] [log] [blame]
// Copyright 2010 Google Inc.
//
// 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.google.enterprise.secmgr.config;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.enterprise.secmgr.json.ProxyTypeAdapter;
import com.google.enterprise.secmgr.json.TypeProxy;
import com.google.gson.GsonBuilder;
import com.google.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* A basic implementation of FlexAuthorizer. This provides the primary logic
* for everything except serialization. The routing table is stored as a list
* and the priority order is the ordering in the list. Each row has a unique
* identifier. The rule table is stored as a map and the key is the display
* name of the row.
*
* @author meghna@google.com (Meghna Dhar)
*/
public final class FlexAuthorizerImpl implements FlexAuthorizer {
private static final String ROOT_URL_PATTERN = "/";
private final Map<String, FlexAuthzRule> ruleTable;
private final List<FlexAuthzRoutingTableEntry> routingTable;
@Inject
private FlexAuthorizerImpl() {
ruleTable = Maps.newHashMap();
routingTable = Lists.newArrayList();
}
@VisibleForTesting
FlexAuthorizerImpl(Map<String, FlexAuthzRule> ruleTable,
List<FlexAuthzRoutingTableEntry> routingTable) {
this.ruleTable = ruleTable;
this.routingTable = routingTable;
}
@Override
public void addToRoutingTable(int order, FlexAuthzRoutingTableEntry entry) {
checkPriorityOrder(order, routingTable.size());
checkForMatchingRule(entry.getAuthzRule());
routingTable.add(order, entry);
}
@Override
public void addToRoutingTable(FlexAuthzRoutingTableEntry entry) {
checkForMatchingRule(entry.getAuthzRule());
routingTable.add(entry);
}
@Override
public void addToRulesTable(FlexAuthzRule rule) {
String name = rule.getRowDisplayName();
Preconditions.checkArgument(ruleTable.get(name) == null,
"Rule already exists: %s", name);
ruleTable.put(name, rule);
}
@Override
public void clearTables() {
routingTable.clear();
ruleTable.clear();
}
@Override
public void deleteFromRoutingTable(UUID uuid) {
Preconditions.checkNotNull(uuid);
int index = getRoutingPriorityOrder(uuid);
Preconditions.checkArgument(index >= 0, "Entry not found in routing table: %s", uuid);
routingTable.remove(index);
}
@Override
public void deleteFromRulesTable(String name) {
Preconditions.checkNotNull(name);
FlexAuthzRule rule = ruleTable.get(name);
Preconditions.checkArgument(rule != null, "Rule isn't in table: %s", name);
for (FlexAuthzRoutingTableEntry entry : routingTable) {
Preconditions.checkState(entry.getAuthzRule() != rule,
"Rule '%s' is referred to by one or more routing-table entries: %s",
name, entry.getUniqueRowId().toString());
}
ruleTable.remove(name);
}
@Override
public List<FlexAuthzRoutingTableEntry> getAllRoutingTable() {
return ImmutableList.copyOf(routingTable);
}
@Override
public List<FlexAuthzRule> getAllRulesTable() {
return ImmutableList.copyOf(ruleTable.values());
}
@Override
public int getRoutingTableSize() {
return routingTable.size();
}
@Override
public FlexAuthzRoutingTableEntry getFromRoutingTable(UUID uuid) {
Preconditions.checkNotNull(uuid);
for (FlexAuthzRoutingTableEntry entry : routingTable) {
if (uuid.equals(entry.getUniqueRowId())) {
return entry;
}
}
return null;
}
@Override
public FlexAuthzRule getFromRulesTable(String name) {
Preconditions.checkNotNull(name);
return ruleTable.get(name);
}
@Override
public void updateRoutingTable(int order, FlexAuthzRoutingTableEntry entry) {
checkPriorityOrder(order, routingTable.size() - 1);
checkForMatchingRule(entry.getAuthzRule());
UUID uuid = entry.getUniqueRowId();
int oldIndex = getRoutingPriorityOrder(uuid);
Preconditions.checkArgument(oldIndex >= 0, "Entry not found in routing table: %s", uuid);
// oldIndex is valid in table prior to change; order is where we want the
// entry to be after the change. So do the delete first, then the insert.
routingTable.remove(oldIndex);
routingTable.add(order, entry);
}
@Override
public void updateRulesTable(FlexAuthzRule rule) {
String name = rule.getRowDisplayName();
FlexAuthzRule oldRule = ruleTable.get(name);
Preconditions.checkArgument(oldRule != null, "Rule not in table: %s", name);
ruleTable.put(name, rule);
// Now update the routing table, replacing the old rule with the new.
for (int i = 0; i < routingTable.size(); i += 1) {
FlexAuthzRoutingTableEntry entry = routingTable.get(i);
if (entry.getAuthzRule() == oldRule) {
routingTable.set(i,
new FlexAuthzRoutingTableEntry(
entry.getUrlPattern(),
rule,
entry.getUniqueRowId()));
}
}
}
public int getRoutingPriorityOrder(UUID uuid) {
Preconditions.checkNotNull(uuid);
int index = 0;
for (FlexAuthzRoutingTableEntry entry : routingTable) {
if (entry.getUniqueRowId().equals(uuid)) {
return index;
}
index++;
}
return -1;
}
private void checkPriorityOrder(int order, int limit) {
Preconditions.checkArgument(order >= 0 && order <= limit,
"Priority order not in valid range: %d", order);
}
private void checkForMatchingRule(FlexAuthzRule rule) {
String ruleName = rule.getRowDisplayName();
Preconditions.checkArgument(ruleTable.get(ruleName) == rule,
"Rule in table doesn't match: %s", ruleName);
}
@Override
public String toString() {
return ConfigSingleton.getGson().toJson(this);
}
@Override
public synchronized boolean equals(Object object) {
if (object == this) { return true; }
if (!(object instanceof FlexAuthorizerImpl)) { return false; }
FlexAuthorizerImpl other = (FlexAuthorizerImpl) object;
return Objects.equal(getAllRulesTable(), other.getAllRulesTable())
&& Objects.equal(getAllRoutingTable(), other.getAllRoutingTable());
}
@Override
public synchronized int hashCode() {
return Objects.hashCode(getAllRulesTable(), getAllRoutingTable());
}
public static FlexAuthorizer makeDefault() {
return makeDefault(null, false);
}
public static FlexAuthorizer makeDefault(String authzServiceUrl, boolean samlUseBatchedRequests) {
FlexAuthorizer flexAuthorizer = new FlexAuthorizerImpl();
int index = 0;
makeDefaultRule(flexAuthorizer, index++, ROOT_URL_PATTERN,
AuthzMechanism.CACHE,
FlexAuthzRule.EMPTY_AUTHN_ID);
makeDefaultRule(flexAuthorizer, index++, ROOT_URL_PATTERN,
AuthzMechanism.POLICY,
FlexAuthzRule.LEGACY_AUTHN_ID);
if (!Strings.isNullOrEmpty(authzServiceUrl)) {
makeDefaultRule(flexAuthorizer, index++, ROOT_URL_PATTERN,
AuthzMechanism.SAML,
FlexAuthzRule.LEGACY_AUTHN_ID,
ImmutableMap.of(
FlexAuthzRule.ParamName.SAML_ENTITY_ID, FlexAuthzRule.LEGACY_SAML_ENTITY_ID,
FlexAuthzRule.ParamName.SAML_USE_BATCHED_REQUESTS,
Boolean.toString(samlUseBatchedRequests)));
}
makeDefaultRule(flexAuthorizer, index++, FlexAuthzRule.LEGACY_CONNECTOR_URL_PATTERN,
AuthzMechanism.CONNECTOR,
FlexAuthzRule.LEGACY_AUTHN_ID,
ImmutableMap.of(
FlexAuthzRule.ParamName.CONNECTOR_NAME, FlexAuthzRule.EMPTY_CONNECTOR_NAME));
makeDefaultRule(flexAuthorizer, index++, ROOT_URL_PATTERN,
AuthzMechanism.HEADREQUEST,
FlexAuthzRule.LEGACY_AUTHN_ID);
return flexAuthorizer;
}
private static void makeDefaultRule(FlexAuthorizer flexAuthorizer, int index, String urlPattern,
AuthzMechanism authzMechType, String authnId,
Map<FlexAuthzRule.ParamName, String> mechSpecificParams) {
FlexAuthzRule rule
= new FlexAuthzRule(authnId, authzMechType, mechSpecificParams, String.valueOf(index),
FlexAuthzRule.NO_TIME_LIMIT);
flexAuthorizer.addToRulesTable(rule);
flexAuthorizer.addToRoutingTable(new FlexAuthzRoutingTableEntry(urlPattern, rule));
}
private static void makeDefaultRule(FlexAuthorizer flexAuthorizer, int index, String urlPattern,
AuthzMechanism authzMechType, String authnId) {
FlexAuthzRule rule
= new FlexAuthzRule(authnId, authzMechType, String.valueOf(index),
FlexAuthzRule.NO_TIME_LIMIT);
flexAuthorizer.addToRulesTable(rule);
flexAuthorizer.addToRoutingTable(new FlexAuthzRoutingTableEntry(urlPattern, rule));
}
static void registerTypeAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(FlexAuthorizer.class,
ProxyTypeAdapter.make(FlexAuthorizer.class, LocalProxy.class));
}
private static final class LocalProxy implements TypeProxy<FlexAuthorizer> {
List<FlexAuthzRoutingTableEntry> entries;
@SuppressWarnings("unused")
LocalProxy() {
}
@SuppressWarnings("unused")
LocalProxy(FlexAuthorizer flexAuthorizer) {
entries = flexAuthorizer.getAllRoutingTable();
}
@Override
public FlexAuthorizer build() {
Map<String, FlexAuthzRule> ruleTable = Maps.newHashMap();
for (FlexAuthzRoutingTableEntry entry : entries) {
FlexAuthzRule rule = entry.getAuthzRule();
ruleTable.put(rule.getRowDisplayName(), rule);
}
return new FlexAuthorizerImpl(ruleTable, entries);
}
}
}