001    package org.kuali.common.aws.ec2.model.security;
002    
003    import static com.google.common.collect.Lists.newArrayList;
004    import static org.kuali.common.util.ListUtils.equalElements;
005    
006    import java.util.Collections;
007    import java.util.Comparator;
008    import java.util.List;
009    
010    import org.hibernate.validator.constraints.NotEmpty;
011    import org.kuali.common.core.build.ValidatingBuilder;
012    import org.kuali.common.core.validate.annotation.IdiotProofImmutable;
013    import org.kuali.common.core.validate.annotation.ValidPort;
014    import org.kuali.common.util.ObjectUtils;
015    
016    import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
017    import com.google.common.base.Objects;
018    import com.google.common.collect.ComparisonChain;
019    import com.google.common.collect.ImmutableList;
020    
021    /**
022     * This is an immutable, slimmed down, version of the comparable AWS permissions object. Only supports a single port (no port ranges) and CIDR notations.
023     */
024    @IdiotProofImmutable
025    @JsonDeserialize(builder = Permission.Builder.class)
026    public final class Permission {
027    
028            @ValidPort
029            private final int port;
030    
031            private final Protocol protocol;
032    
033            @NotEmpty
034            private final ImmutableList<String> cidrNotations;
035    
036            public static Permission create(int port) {
037                    return builder(port).build();
038            }
039    
040            public static Builder builder(int port) {
041                    return new Builder(port);
042            }
043    
044            public static class Builder extends ValidatingBuilder<Permission> {
045    
046                    // Required
047                    private final int port;
048    
049                    // Optional
050                    private List<String> cidrNotations = newArrayList(CIDR.ANY.getNotation());
051                    private Protocol protocol = Protocol.TCP;
052    
053                    public Builder(int port) {
054                            this.port = port;
055                    }
056    
057                    public Builder withCidrNotations(List<String> cidrNotations) {
058                            this.cidrNotations = cidrNotations;
059                            return this;
060                    }
061    
062                    public Builder withProtocol(Protocol protocol) {
063                            this.protocol = protocol;
064                            return this;
065                    }
066    
067                    @Override
068                    public Permission build() {
069                            // Changing this has implications for equals(), be careful
070                            this.cidrNotations = newArrayList(cidrNotations);
071                            Collections.sort(cidrNotations);
072                            return validate(new Permission(this));
073                    }
074            }
075    
076            private Permission(Builder builder) {
077                    this.protocol = builder.protocol;
078                    this.port = builder.port;
079                    this.cidrNotations = ImmutableList.copyOf(builder.cidrNotations);
080            }
081    
082            public Protocol getProtocol() {
083                    return protocol;
084            }
085    
086            public int getPort() {
087                    return port;
088            }
089    
090            public List<String> getCidrNotations() {
091                    return cidrNotations;
092            }
093    
094            @Override
095            public int hashCode() {
096                    final int prime = 31;
097                    int result = Objects.hashCode(port, protocol);
098                    for (String notation : cidrNotations) {
099                            result = result * prime + notation.hashCode();
100                    }
101                    return result;
102            }
103    
104            @Override
105            public boolean equals(Object object) {
106    
107                    // They are the same object
108                    if (this == object) {
109                            return true;
110                    }
111    
112                    // Make sure object isn't null and is a Permission object
113                    if (ObjectUtils.notEqual(this, object)) {
114                            return false;
115                    }
116    
117                    // Cast to a Permission object
118                    Permission a = this;
119                    Permission b = (Permission) object;
120    
121                    // If they both have the exact same list of CIDR notations, they are equal
122                    // This only works because Builder.build() sorts the CIDR notation list
123                    return Objects.equal(a.port, b.port) && Objects.equal(a.protocol, b.protocol) && equalElements(a.cidrNotations, b.cidrNotations);
124    
125            }
126    
127            // Singleton enum pattern
128            public enum DefaultComparator implements Comparator<Permission> {
129                    INSTANCE;
130    
131                    @Override
132                    public int compare(Permission one, Permission two) {
133                            return ComparisonChain.start().compare(one.getPort(), two.getPort()).compare(one.getProtocol(), two.getProtocol()).result();
134                    }
135            }
136    
137    }