blob: 84c6eaa447e8a1fe0e7a088e923c454fde7f3b62 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.solr.handler.component;
import com.google.common.base.Joiner;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Set;
public class QueryDocAuthorizationComponent extends DocAuthorizationComponent {
private static final Logger LOG =
LoggerFactory.getLogger(QueryDocAuthorizationComponent.class);
public static final String AUTH_FIELD_PROP = "sentryAuthField";
public static final String DEFAULT_AUTH_FIELD = "sentry_auth";
public static final String ALL_ROLES_TOKEN_PROP = "allRolesToken";
public static final String ENABLED_PROP = "enabled";
public static final String MODE_PROP = "matchMode";
public static final String DEFAULT_MODE_PROP = MatchType.DISJUNCTIVE.toString();
public static final String ALLOW_MISSING_VAL_PROP = "allow_missing_val";
public static final String TOKEN_COUNT_PROP = "tokenCountField";
public static final String DEFAULT_TOKEN_COUNT_FIELD_PROP = "sentry_auth_count";
public static final String QPARSER_PROP = "qParser";
private String authField;
private String allRolesToken;
private boolean enabled;
private MatchType matchMode;
private String tokenCountField;
private boolean allowMissingValue;
private String qParserName;
private enum MatchType {
DISJUNCTIVE,
CONJUNCTIVE
}
@Override
public void init(NamedList args) {
SolrParams params = args.toSolrParams();
this.authField = params.get(AUTH_FIELD_PROP, DEFAULT_AUTH_FIELD);
LOG.info("QueryDocAuthorizationComponent authField: {}", this.authField);
this.allRolesToken = params.get(ALL_ROLES_TOKEN_PROP, "");
LOG.info("QueryDocAuthorizationComponent allRolesToken: {}", this.allRolesToken);
this.enabled = params.getBool(ENABLED_PROP, false);
LOG.info("QueryDocAuthorizationComponent enabled: {}", this.enabled);
this.matchMode = MatchType.valueOf(params.get(MODE_PROP, DEFAULT_MODE_PROP).toUpperCase());
LOG.info("QueryDocAuthorizationComponent matchType: {}", this.matchMode);
if (this.matchMode == MatchType.CONJUNCTIVE) {
this.qParserName = params.get(QPARSER_PROP, "subset").trim();
LOG.debug("QueryDocAuthorizationComponent qParserName: {}", this.qParserName);
this.allowMissingValue = params.getBool(ALLOW_MISSING_VAL_PROP, false);
LOG.debug("QueryDocAuthorizationComponent allowMissingValue: {}", this.allowMissingValue);
this.tokenCountField = params.get(TOKEN_COUNT_PROP, DEFAULT_TOKEN_COUNT_FIELD_PROP);
LOG.debug("QueryDocAuthorizationComponent tokenCountField: {}", this.tokenCountField);
}
}
private void addDisjunctiveRawClause(StringBuilder builder, String value) {
// requires a space before the first term, so the
// default lucene query parser will be used
builder.append(" {!raw f=").append(authField).append(" v=")
.append(value).append("}");
}
public String getDisjunctiveFilterQueryStr(Set<String> roles) {
if (roles != null && !roles.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (String role : roles) {
addDisjunctiveRawClause(builder, role);
}
if (allRolesToken != null && !allRolesToken.isEmpty()) {
addDisjunctiveRawClause(builder, allRolesToken);
}
return builder.toString();
}
return null;
}
@Override
public void prepare(ResponseBuilder rb, String userName) throws IOException {
Set<String> roles = getRoles(rb.req, userName);
if (roles != null && !roles.isEmpty()) {
String filterQuery;
if (matchMode == MatchType.DISJUNCTIVE) {
filterQuery = getDisjunctiveFilterQueryStr(roles);
} else {
filterQuery = getConjunctiveFilterQueryStr(roles);
}
ModifiableSolrParams newParams = new ModifiableSolrParams(rb.req.getParams());
newParams.add("fq", filterQuery);
rb.req.setParams(newParams);
if (LOG.isDebugEnabled()) {
LOG.debug("Adding filter query {} for user {} with roles {}", filterQuery, userName, roles);
}
} else {
throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED,
"Request from user: " + userName + " rejected because user is not associated with any roles");
}
}
private String getConjunctiveFilterQueryStr(Set<String> roles) {
StringBuilder filterQuery = new StringBuilder();
filterQuery
.append(" {!").append(qParserName)
.append(" set_field=\"").append(authField).append("\"")
.append(" set_value=\"").append(Joiner.on(',').join(roles.iterator())).append("\"")
.append(" count_field=\"").append(tokenCountField).append("\"");
if (allRolesToken != null && !allRolesToken.equals("")) {
filterQuery.append(" wildcard_token=\"").append(allRolesToken).append("\"");
}
filterQuery.append(" allow_missing_val=").append(allowMissingValue).append(" }");
return filterQuery.toString();
}
@Override
public void process(ResponseBuilder rb) throws IOException {
}
@Override
public String getDescription() {
return "Handle Query Document Authorization";
}
@Override
public boolean getEnabled() {
return enabled;
}
}