/* (c) 2014, 2015 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.geofence.core.model; import org.geoserver.geofence.core.model.adapter.FKGSInstanceAdapter; import org.geoserver.geofence.core.model.enums.GrantType; import java.io.Serializable; import javax.persistence.AttributeOverride; import javax.persistence.AttributeOverrides; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.ForeignKey; import org.hibernate.annotations.Index; /** * A Rule expresses if a given combination of request access is allowed or not. * <P> * In a given Rule, you may specify a precise combination of filters or a general * behavior. <BR> * Filtering can be done on <UL> * <LI> the requesting user </LI> * <LI> the profile associated to the requesting user</LI> * <LI> the instance of the accessed geoserver</LI> * <LI> the accessed service (e.g.: WMS)</LI> * <LI> the requested operation inside the accessed service (e.g.: getMap)</LI> * <LI> the workspace in geoserver</LI> * <LI> the requested layer </LI> * </UL> * <P><B>Example</B>: In order to allow access to every request to the WMS service in the instance GS1, * you will need to create a Rule, by only setting Service=WMS and Instance=GS1, * leaving the other fields to <TT>null</TT>. * <P> * When an access has to be checked for filtering, all the matching rules are read; * they are then evaluated according to their priority: the first rule found having * accessType <TT><B>{@link GrantType#ALLOW}</B></TT> or <TT><B>{@link GrantType#DENY}</B></TT> wins, * and the access is granted or denied accordingly. * <BR>Matching rules with accessType=<TT><B>{@link GrantType#LIMIT}</B></TT> are collected and evaluated at the end, * only if the request is Allowed by some other rule with lower priority. * <BR>These rules will have an associated {@link RuleLimits RuleLimits} that * defines some restrictions for using the data (such as area limitation). * * @author ETj (etj at geo-solutions.it) */ @Entity(name = "Rule") @Table(name = "gf_rule", uniqueConstraints = { @UniqueConstraint(columnNames = {"username", "rolename", "instance_id", "service", "request", "workspace", "layer"})}) @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "Rule") @XmlRootElement(name = "Rule") @XmlType(propOrder={"id","priority","username","rolename","instance","addressRange","service","request","workspace","layer","access","layerDetails","ruleLimits"}) public class Rule implements Identifiable, Serializable, Prioritizable, IPRangeProvider { private static final long serialVersionUID = -5127129225384707164L; /** The id. */ @Id @GeneratedValue @Column private Long id; /** Lower numbers have higher priority */ @Column(nullable = false) @Index(name = "idx_rule_priority") private long priority; @Column(name = "username", nullable = true) private String username; @Column(name = "rolename", nullable = true) private String rolename; @ManyToOne(optional = true, fetch = FetchType.EAGER) @ForeignKey(name = "fk_rule_instance") private GSInstance instance; @Column @Index(name = "idx_rule_service") private String service; @Embedded @AttributeOverrides({ @AttributeOverride(name="low", column=@Column(name="ip_low")), @AttributeOverride(name="high", column=@Column(name="ip_high")), @AttributeOverride(name="size", column=@Column(name="ip_size")) }) private IPAddressRange addressRange; @Column @Index(name = "idx_rule_request") private String request; @Column @Index(name = "idx_rule_workspace") private String workspace; @Column @Index(name = "idx_rule_layer") private String layer; @Enumerated(EnumType.STRING) @Column(name = "grant_type", nullable = false) private GrantType access; @OneToOne(optional = true, cascade = CascadeType.REMOVE, mappedBy = "rule") // main ref is in LayerDetails @ForeignKey(name = "fk_rule_details") private LayerDetails layerDetails; @OneToOne(optional = true, cascade = CascadeType.REMOVE, mappedBy = "rule") // main ref is in ruleLimits @ForeignKey(name = "fk_rule_limits") private RuleLimits ruleLimits; public Rule() { } public Rule(long priority, String username, String rolename, GSInstance instance, IPAddressRange addressRange, String service, String request, String workspace, String layer, GrantType access) { this.priority = priority; this.username = username; this.rolename = rolename; this.instance = instance; this.addressRange = addressRange; this.service = service; this.request = request; this.workspace = workspace; this.layer = layer; this.access = access; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public IPAddressRange getAddressRange() { return addressRange; } public void setAddressRange(IPAddressRange addressRange) { this.addressRange = addressRange; } public long getPriority() { return priority; } public void setPriority(long priority) { this.priority = priority; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getRolename() { return rolename; } public void setRolename(String rolename) { this.rolename = rolename; } @XmlJavaTypeAdapter(FKGSInstanceAdapter.class) public GSInstance getInstance() { return instance; } public void setInstance(GSInstance instance) { this.instance = instance; } public String getLayer() { return layer; } public void setLayer(String layer) { this.layer = layer; } public String getService() { return service; } public void setService(String service) { this.service = service; } public String getRequest() { return request; } public void setRequest(String request) { this.request = request; } public String getWorkspace() { return workspace; } public void setWorkspace(String workspace) { this.workspace = workspace; } public GrantType getAccess() { return access; } public void setAccess(GrantType access) { this.access = access; } public RuleLimits getRuleLimits() { return ruleLimits; } /** * @deprecated This setter is only used by hibernate, should not be called by the user. * @param ruleLimits */ public void setRuleLimits(RuleLimits ruleLimits) { this.ruleLimits = ruleLimits; } public LayerDetails getLayerDetails() { return layerDetails; } /** * @deprecated This setter is only used by hibernate, should not be called by the user. */ public void setLayerDetails(LayerDetails layerDetails) { this.layerDetails = layerDetails; } @Override public String toString() { StringBuilder sb = new StringBuilder(getClass().getSimpleName()) .append("[id:").append(id) .append(" pri:").append(priority); if (username != null) { sb.append(" user:").append(prepare(username)); } if (rolename != null) { sb.append(" role:").append(prepare(rolename)); } if (instance != null) { sb.append(" iId:").append(instance.getId()); sb.append(" iName:").append(prepare(instance.getName())); } if (addressRange != null) { sb.append(" addr:").append(addressRange.getCidrSignature()); } if (service != null) { sb.append(" srv:").append(service); } if (request != null) { sb.append(" req:").append(request); } if (workspace != null) { sb.append(" ws:").append(workspace); } if (layer != null) { sb.append(" l:").append(layer); } sb.append(" acc:").append(access); sb.append(']'); return sb.toString(); } private static String prepare(String s) { if(s==null) return "(null)"; else if (s.isEmpty()) return "(empty)"; else return s; } }