package org.swellrt.beta.model; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Consumer; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.wave.ParticipantId; /** * Utilities to manage access control tokens. * <p> * <br> * Access control token syntax is "{@code r[]w[tom@local.net,john@master.org]}" * where: * <li>if token is empty, access is granted for everyone, read and write</li> * <li>if "r" part is empty or not exist, everyone can read</li> * <li>if "r" part is not empty, read access is only granted for users in the list</li> * <li>if "w" part is empty or not exist, everyone can write</li> * <li>if "w" part is not empty, write access is only granted for users in the list</li> * <li>if there is a "!w" mark, the node is read only, and "w" list is ignored</li> * * @author pablojan@gmail.com (Pablo Ojanguren) * */ public class SNodeAccessControl { public static final String ACCESS_TOKEN_READ_MARK = "r["; public static final String ACCESS_TOKEN_WRITE_MARK = "w["; public static final String ACCESS_TOKEN_READONLY_MARK = "!w"; public static boolean isToken(String s) { return s != null && (s.startsWith(ACCESS_TOKEN_READ_MARK) || s.startsWith(ACCESS_TOKEN_WRITE_MARK) || s.startsWith(ACCESS_TOKEN_READONLY_MARK)); } public static class Builder { List<String> readers = new ArrayList<String>(); List<String> writers = new ArrayList<String>(); boolean isReadOnly = false; public Builder read(String[] participants) { if (participants != null && participants.length > 0) for (int i = 0; i < participants.length; i++) readers.add(participants[i]); return this; } public Builder write(String[] participants) { if (participants != null && participants.length > 0) for (int i = 0; i < participants.length; i++) writers.add(participants[i]); return this; } public Builder read(String participantId) { if (participantId != null) readers.add(participantId); return this; } public Builder write(String participantId) { if (participantId != null) writers.add(participantId); return this; } public Builder setReadOnly(Boolean isReadOnly) { if (isReadOnly != null) this.isReadOnly = isReadOnly; return this; } public SNodeAccessControl build() { return new SNodeAccessControl(readers, writers, isReadOnly); } } public static SNodeAccessControl deserialize(String s) { if (s == null) return null; boolean isReadOnly = s.indexOf("!w") >= 0; int rbegin = s.indexOf("r["); String[] readers; if (rbegin >= 0) { int rend = s.indexOf("]", rbegin); String rstr = s.substring(rbegin+2, rend); readers = rstr.split(","); } else { readers = new String[0]; } int wbegin = s.indexOf("w["); String[] writers; if (wbegin >= 0) { int wend = s.indexOf("]", wbegin); String wstr = s.substring(wbegin+2, wend); writers = wstr.split(","); } else { writers = new String[0]; } return new SNodeAccessControl(Arrays.asList(readers), Arrays.asList(writers), isReadOnly); } private final Set<String> readers = new HashSet<String>(); private final Set<String> writers = new HashSet<String>(); private boolean isReadOnly = false; public SNodeAccessControl(Collection<String> readers, Collection<String> writers, boolean isReadOnly) { this.readers.addAll(readers); this.writers.addAll(writers); this.isReadOnly = isReadOnly; } public SNodeAccessControl(boolean isReadOnly) { this.isReadOnly = isReadOnly; } public SNodeAccessControl() { } public boolean canWrite(ParticipantId participantId) { return canWrite(participantId.getAddress()); } public boolean canWrite(String participantId) { return !isReadOnly && (writers.isEmpty() || writers.contains(participantId)) ; } public boolean canRead(ParticipantId participantId) { return canRead(participantId.getAddress()); } public boolean canRead(String participantId) { return readers.isEmpty() || readers.contains(participantId); } public String serialize() { String w = ""; if (!writers.isEmpty()) { w+="w["; for (String p: writers) { if (w.length() > 2) w += ","; w+=p; } w+="]"; } if (isReadOnly) if (w.length() > 0) w = "!"+w; else w = "!w"; String r = ""; if (!readers.isEmpty()) { r+="r["; for (String p: readers) { if (r.length() > 2) r += ","; r+=p; } r+="]"; } return r+w; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (isReadOnly ? 1231 : 1237); result = prime * result + ((readers == null) ? 0 : readers.hashCode()); result = prime * result + ((writers == null) ? 0 : writers.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SNodeAccessControl other = (SNodeAccessControl) obj; if (isReadOnly != other.isReadOnly) { return false; } if (readers == null) { if (other.readers != null) { return false; } } else if (readers.size() != other.readers.size()) { return false; } else { for (String s: readers) { if (!other.readers.contains(s)) return false; } } if (writers == null) { if (other.writers != null) { return false; } } else if (writers.size() != other.writers.size()) { return false; } else { for (String s: writers) { if (!other.writers.contains(s)) return false; } } return true; } }