package io.fathom.cloud.storage.api.os.models; import java.net.URI; import java.util.List; import java.util.Set; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Sets; /** * This logic is meant to mirror this: * https://github.com/openstack/swift/blob/master/swift/common/middleware/acl.py * */ public class StorageAcl { private static final Set<String> REF_TAGS = Sets.newHashSet(".r", ".ref", ".referer", ".referrer"); private final List<UserAcl> users; private final List<ReferrerAcl> referers; static class UserAcl { final String group; final String user; public UserAcl(String group, String user) { this.group = group; this.user = user; } @Override public String toString() { if (!Strings.isNullOrEmpty(group)) { return group + ":" + user; } else { return user; } } } static class ReferrerAcl { final boolean deny; final String referer; public ReferrerAcl(boolean deny, String referer) { this.deny = deny; this.referer = referer; } @Override public String toString() { return ".r:" + (deny ? "-" : "") + referer; } } public StorageAcl(List<UserAcl> users, List<ReferrerAcl> referers) { this.users = users; this.referers = referers; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(Joiner.on(',').join(users)); if (sb.length() != 0) { sb.append(","); } sb.append(Joiner.on(',').join(referers)); return sb.toString(); } public enum AclType { Read, Write } public static StorageAcl parse(AclType aclType, String s) { List<UserAcl> users = Lists.newArrayList(); List<ReferrerAcl> referers = Lists.newArrayList(); for (String token : Splitter.on(',').split(s)) { token = token.trim(); if (!token.isEmpty()) { int colonIndex = token.indexOf(':'); if (colonIndex == -1) { users.add(new UserAcl(null, token)); } else { String first = token.substring(0, colonIndex).trim(); String second = token.substring(colonIndex + 1).trim(); if (Strings.isNullOrEmpty(first) || first.charAt(0) != '.') { users.add(new UserAcl(first, second)); } else if (REF_TAGS.contains(first)) { if (aclType != AclType.Read) { throw new IllegalArgumentException("Refererrers not allowed in write ACL: " + token); } boolean deny = false; if (second.startsWith("-")) { deny = true; second = second.substring(1).trim(); } if (second.startsWith("*") && !second.equals("*")) { second = second.substring(1).trim(); } if (second.isEmpty() || second.equals(".")) { throw new IllegalArgumentException("No host/domain value in ACL: " + token); } referers.add(new ReferrerAcl(deny, second)); } else { throw new IllegalArgumentException("Unknown designator in ACL: " + token); } } } } return new StorageAcl(users, referers); } public boolean isRefererAllowed(String referer) { if (!referers.isEmpty()) { String hostname = null; if (referer != null) { referer = referer.trim(); URI uri = URI.create(referer); hostname = uri.getHost(); } if (hostname != null) { hostname = hostname.toLowerCase(); } else { hostname = "unknown"; } for (ReferrerAcl acl : referers) { boolean isMatch = false; String find = acl.referer; if (find.equals("*") || find.equals(hostname)) { isMatch = true; } else if (find.startsWith(".") && hostname.endsWith(find)) { isMatch = true; } if (isMatch) { if (acl.deny) { return false; } else { return true; } } } } return false; } }