| // Copyright 2013 Google Inc. All Rights Reserved. |
| // |
| // 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.adaptor; |
| |
| import java.util.Arrays; |
| |
| /** |
| * Represents either a user or a group. |
| */ |
| public abstract class Principal implements Comparable<Principal> { |
| public static final String DEFAULT_NAMESPACE = "Default"; |
| |
| private final String name; |
| private final String namespace; |
| |
| Principal(String n, String ns) { |
| if (null == n || null == ns) { |
| throw new NullPointerException(); |
| } |
| if (n.isEmpty()) { |
| throw new IllegalArgumentException("name cannot be empty"); |
| } |
| if (!n.trim().equals(n)) { |
| throw new IllegalArgumentException("name \"" + n |
| + "\" should not start or end with space"); |
| } |
| name = n; |
| namespace = ns; |
| } |
| |
| Principal(String n) { |
| this(n, DEFAULT_NAMESPACE); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public String getNamespace() { |
| return namespace; |
| } |
| |
| public abstract boolean isUser(); |
| |
| public abstract boolean isGroup(); |
| |
| @Override |
| public boolean equals(Object other) { |
| boolean same = other instanceof Principal; |
| if (same) { |
| Principal p = (Principal) other; |
| same = p.isUser() == isUser() && p.name.equals(name) |
| && p.namespace.equals(namespace); |
| } |
| return same; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Arrays.hashCode(new Object[]{ isUser(), name, namespace }); |
| } |
| |
| @Override |
| public String toString() { |
| String type = isUser() ? "User" : "Group"; |
| return "Principal(" + type + "," + name + "," + namespace + ")"; |
| } |
| |
| /** |
| * Sorts by 1) namespace, 2) user or group, 3) name. |
| */ |
| @Override |
| public int compareTo(Principal other) { |
| int spacecmp = namespace.compareTo(other.namespace); |
| if (0 != spacecmp) { |
| return spacecmp; |
| } |
| // OK, same namespace |
| |
| if (isUser() != other.isUser()) { |
| return isUser() ? -1 : 1; |
| } |
| // OK, same namespace and same type |
| |
| return name.compareTo(other.name); |
| } |
| |
| ParsedPrincipal parse() { |
| for (int i = 0; i < name.length(); i++) { |
| char c = name.charAt(i); |
| switch (c) { |
| case '\\': |
| return new ParsedPrincipal(isGroup(), name.substring(i + 1), |
| name.substring(0, i), DomainFormat.NETBIOS, namespace); |
| case '/': |
| return new ParsedPrincipal(isGroup(), name.substring(i + 1), |
| name.substring(0, i), DomainFormat.NETBIOS_FORWARDSLASH, |
| namespace); |
| case '@': |
| return new ParsedPrincipal(isGroup(), name.substring(0, i), |
| name.substring(i + 1), DomainFormat.DNS, namespace); |
| default: |
| } |
| } |
| return new ParsedPrincipal(isGroup(), name, "", DomainFormat.NONE, |
| namespace); |
| } |
| |
| static enum DomainFormat { |
| NONE, |
| DNS, |
| NETBIOS, |
| /** |
| * Same as NETBIOS, but a forward slash is used instead of a backslash. This |
| * is to support round-tripping all Principals; if you don't modify the |
| * ParsedPrincipal, you shouldn't see any modifications. |
| */ |
| NETBIOS_FORWARDSLASH, |
| ; |
| } |
| |
| /** |
| * Immutable form of Principal where user's name has been split into name and |
| * domain components. This class guarantees full fidelity of Principal |
| * objects: {@code principal.equals(principal.parse().toPrincipal())} is |
| * always {@code true}. |
| */ |
| static class ParsedPrincipal<T extends Principal> { |
| public final boolean isGroup; |
| public final String plainName; |
| public final String domain; |
| public final DomainFormat domainFormat; |
| public final String namespace; |
| |
| public ParsedPrincipal(boolean isGroup, String plainName, String domain, |
| DomainFormat domainFormat, String namespace) { |
| if (plainName == null || domain == null || domainFormat == null |
| || namespace == null) { |
| throw new NullPointerException(); |
| } |
| this.isGroup = isGroup; |
| this.plainName = plainName; |
| this.domain = domain; |
| this.domainFormat = domainFormat; |
| this.namespace = namespace; |
| } |
| |
| public ParsedPrincipal plainName(String plainName) { |
| return new ParsedPrincipal(isGroup, plainName, domain, domainFormat, |
| namespace); |
| } |
| |
| public ParsedPrincipal domain(String domain) { |
| return new ParsedPrincipal(isGroup, plainName, domain, domainFormat, |
| namespace); |
| } |
| |
| public ParsedPrincipal domainFormat(DomainFormat domainFormat) { |
| return new ParsedPrincipal(isGroup, plainName, domain, domainFormat, |
| namespace); |
| } |
| |
| public ParsedPrincipal namespace(String namespace) { |
| return new ParsedPrincipal(isGroup, plainName, domain, domainFormat, |
| namespace); |
| } |
| |
| /** |
| * Determine the format that should be used for combining the name and |
| * domain together. This does not simply return domainFormat, because it |
| * needs to make sure that using such a format will not cause ambiguities. |
| */ |
| private DomainFormat determineEffectiveFormat() { |
| DomainFormat format = domainFormat; |
| if (domain.equals("")) { |
| return format; |
| } |
| |
| // Domain handling |
| if (format == DomainFormat.NONE) { |
| format = DomainFormat.NETBIOS; |
| } |
| if ((format == DomainFormat.NETBIOS |
| || format == DomainFormat.NETBIOS_FORWARDSLASH) |
| && containsSpecial(domain)) { |
| format = DomainFormat.DNS; |
| } |
| if (format == DomainFormat.DNS && containsSpecial(plainName)) { |
| if (containsSpecial(domain)) { |
| throw new IllegalStateException("Neither NETBIOS nor DNS formats can " |
| + "be used: plainName=" + plainName + " domain=" + domain); |
| } |
| format = DomainFormat.NETBIOS; |
| } |
| return format; |
| } |
| |
| private boolean containsSpecial(String s) { |
| return s.contains("\\") || s.contains("/") || s.contains("@"); |
| } |
| |
| public Principal toPrincipal() { |
| String name; |
| switch (determineEffectiveFormat()) { |
| case NONE: |
| name = plainName; |
| break; |
| case DNS: |
| name = plainName + "@" + domain; |
| break; |
| case NETBIOS: |
| name = domain + "\\" + plainName; |
| break; |
| case NETBIOS_FORWARDSLASH: |
| name = domain + "/" + plainName; |
| break; |
| default: |
| throw new AssertionError(); |
| } |
| if (isGroup) { |
| return new GroupPrincipal(name, namespace); |
| } else { |
| return new UserPrincipal(name, namespace); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "ParsedPrincipal(isGroup=" + isGroup + ",plainName=" + plainName |
| + ",domain=" + domain + ",domainFormat=" + domainFormat |
| + ",namespace=" + namespace + ")"; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Arrays.hashCode( |
| new Object[] {isGroup, plainName, domain, domainFormat, namespace}); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof ParsedPrincipal)) { |
| return false; |
| } |
| ParsedPrincipal p = (ParsedPrincipal) o; |
| return isGroup == p.isGroup && plainName.equals(p.plainName) |
| && domainFormat == p.domainFormat && namespace.equals(p.namespace) |
| && domain.equals(p.domain); |
| } |
| } |
| } |