NIFI-2122: - Merging responses for the current user and the flow configuration. - Returning whether NiFi is configured with a policy based authorizer in the flow configuration. - Only showing the users and policy icons when configured with a policy based authorizer. - Failing faster when invoking the users or policies endpoint when not configured with a configurable authorizer.

This closes #736

Signed-off-by: jpercivall <joepercivall@yahoo.com>
This commit is contained in:
Matt Gilman 2016-07-28 16:22:58 -04:00 committed by jpercivall
parent 5e4ba04589
commit da238b16ef
17 changed files with 272 additions and 18 deletions

View File

@ -29,6 +29,7 @@ import java.util.Date;
@XmlType(name = "flowConfiguration") @XmlType(name = "flowConfiguration")
public class FlowConfigurationDTO { public class FlowConfigurationDTO {
private Boolean supportsConfigurableAuthorizer;
private Long autoRefreshIntervalSeconds; private Long autoRefreshIntervalSeconds;
private Date currentTime; private Date currentTime;
@ -49,6 +50,21 @@ public class FlowConfigurationDTO {
this.autoRefreshIntervalSeconds = autoRefreshIntervalSeconds; this.autoRefreshIntervalSeconds = autoRefreshIntervalSeconds;
} }
/**
* @return whether this NiFi supports a configurable authorizer. This value is read only
*/
@ApiModelProperty(
value = "Whether this NiFi supports a configurable authorizer.",
readOnly = true
)
public Boolean getSupportsConfigurableAuthorizer() {
return supportsConfigurableAuthorizer;
}
public void setSupportsConfigurableAuthorizer(Boolean supportsConfigurableAuthorizer) {
this.supportsConfigurableAuthorizer = supportsConfigurableAuthorizer;
}
/** /**
* @return current time on the server * @return current time on the server
*/ */

View File

@ -28,7 +28,9 @@ import org.apache.nifi.cluster.coordination.http.endpoints.ControllerServiceRefe
import org.apache.nifi.cluster.coordination.http.endpoints.ControllerServicesEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.ControllerServicesEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.ControllerStatusEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.ControllerStatusEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.CountersEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.CountersEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.CurrentUserEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.DropRequestEndpiontMerger; import org.apache.nifi.cluster.coordination.http.endpoints.DropRequestEndpiontMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.FlowConfigurationEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.FlowMerger; import org.apache.nifi.cluster.coordination.http.endpoints.FlowMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.FlowSnippetEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.FlowSnippetEndpointMerger;
import org.apache.nifi.cluster.coordination.http.endpoints.GroupStatusEndpointMerger; import org.apache.nifi.cluster.coordination.http.endpoints.GroupStatusEndpointMerger;
@ -105,6 +107,8 @@ public class StandardHttpResponseMerger implements HttpResponseMerger {
endpointMergers.add(new SystemDiagnosticsEndpointMerger()); endpointMergers.add(new SystemDiagnosticsEndpointMerger());
endpointMergers.add(new CountersEndpointMerger()); endpointMergers.add(new CountersEndpointMerger());
endpointMergers.add(new FlowMerger()); endpointMergers.add(new FlowMerger());
endpointMergers.add(new CurrentUserEndpointMerger());
endpointMergers.add(new FlowConfigurationEndpointMerger());
endpointMergers.add(new TemplatesEndpointMerger()); endpointMergers.add(new TemplatesEndpointMerger());
} }

View File

@ -0,0 +1,65 @@
/*
* 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.nifi.cluster.coordination.http.endpoints;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.web.api.dto.PermissionsDTO;
import org.apache.nifi.web.api.entity.CurrentUserEntity;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public class CurrentUserEndpointMerger extends AbstractSingleEntityEndpoint<CurrentUserEntity> {
public static final Pattern CURRENT_USER_URI_PATTERN = Pattern.compile("/nifi-api/flow/current-user");
@Override
public boolean canHandle(URI uri, String method) {
return "GET".equalsIgnoreCase(method) && CURRENT_USER_URI_PATTERN.matcher(uri.getPath()).matches();
}
@Override
protected Class<CurrentUserEntity> getEntityClass() {
return CurrentUserEntity.class;
}
@Override
protected void mergeResponses(final CurrentUserEntity clientEntity, final Map<NodeIdentifier, CurrentUserEntity> entityMap,
final Set<NodeResponse> successfulResponses, final Set<NodeResponse> problematicResponses) {
for (final Map.Entry<NodeIdentifier, CurrentUserEntity> entry : entityMap.entrySet()) {
final CurrentUserEntity entity = entry.getValue();
if (entity != clientEntity) {
mergePermissions(clientEntity.getControllerPermissions(), entity.getControllerPermissions());
mergePermissions(clientEntity.getCountersPermissions(), entity.getCountersPermissions());
mergePermissions(clientEntity.getPoliciesPermissions(), entity.getPoliciesPermissions());
mergePermissions(clientEntity.getProvenancePermissions(), entity.getProvenancePermissions());
mergePermissions(clientEntity.getTenantsPermissions(), entity.getTenantsPermissions());
}
}
}
private void mergePermissions(PermissionsDTO clientPermissions, PermissionsDTO permissions) {
clientPermissions.setCanRead(clientPermissions.getCanRead() && permissions.getCanRead());
clientPermissions.setCanWrite(clientPermissions.getCanWrite() && permissions.getCanWrite());
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.nifi.cluster.coordination.http.endpoints;
import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.web.api.dto.FlowConfigurationDTO;
import org.apache.nifi.web.api.entity.FlowConfigurationEntity;
import java.net.URI;
import java.util.Map;
import java.util.regex.Pattern;
public class FlowConfigurationEndpointMerger extends AbstractNodeStatusEndpoint<FlowConfigurationEntity, FlowConfigurationDTO> {
public static final Pattern FLOW_CONFIGURATION_URI_PATTERN = Pattern.compile("/nifi-api/flow/config");
@Override
public boolean canHandle(URI uri, String method) {
return "GET".equalsIgnoreCase(method) && FLOW_CONFIGURATION_URI_PATTERN.matcher(uri.getPath()).matches();
}
@Override
protected Class<FlowConfigurationEntity> getEntityClass() {
return FlowConfigurationEntity.class;
}
@Override
protected FlowConfigurationDTO getDto(FlowConfigurationEntity entity) {
return entity.getFlowConfiguration();
}
@Override
protected void mergeResponses(FlowConfigurationDTO clientDto, Map<NodeIdentifier, FlowConfigurationDTO> dtoMap, NodeIdentifier selectedNodeId) {
for (final Map.Entry<NodeIdentifier, FlowConfigurationDTO> entry : dtoMap.entrySet()) {
final NodeIdentifier nodeId = entry.getKey();
final FlowConfigurationDTO toMerge = entry.getValue();
if (toMerge != clientDto) {
clientDto.setSupportsConfigurableAuthorizer(clientDto.getSupportsConfigurableAuthorizer() && toMerge.getSupportsConfigurableAuthorizer());
}
}
}
}

View File

@ -23,6 +23,7 @@ import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.ApiResponses;
import com.wordnik.swagger.annotations.Authorization; import com.wordnik.swagger.annotations.Authorization;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.Authorizable;
@ -37,6 +38,7 @@ import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.entity.AccessPolicyEntity; import org.apache.nifi.web.api.entity.AccessPolicyEntity;
import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter; import org.apache.nifi.web.api.request.LongParameter;
import org.apache.nifi.web.dao.AccessPolicyDAO;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -130,6 +132,11 @@ public class AccessPolicyResource extends ApplicationResource {
required = true required = true
) @PathParam("resource") String rawResource) { ) @PathParam("resource") String rawResource) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
// parse the action and resource type // parse the action and resource type
final RequestAction requestAction = RequestAction.valueOfValue(action); final RequestAction requestAction = RequestAction.valueOfValue(action);
final String resource = "/" + rawResource; final String resource = "/" + rawResource;
@ -189,6 +196,11 @@ public class AccessPolicyResource extends ApplicationResource {
required = true required = true
) final AccessPolicyEntity accessPolicyEntity) { ) final AccessPolicyEntity accessPolicyEntity) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (accessPolicyEntity == null || accessPolicyEntity.getComponent() == null) { if (accessPolicyEntity == null || accessPolicyEntity.getComponent() == null) {
throw new IllegalArgumentException("Access policy details must be specified."); throw new IllegalArgumentException("Access policy details must be specified.");
} }
@ -277,6 +289,11 @@ public class AccessPolicyResource extends ApplicationResource {
) )
@PathParam("id") final String id) { @PathParam("id") final String id) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.GET); return replicate(HttpMethod.GET);
} }
@ -335,6 +352,11 @@ public class AccessPolicyResource extends ApplicationResource {
required = true required = true
) final AccessPolicyEntity accessPolicyEntity) { ) final AccessPolicyEntity accessPolicyEntity) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (accessPolicyEntity == null || accessPolicyEntity.getComponent() == null) { if (accessPolicyEntity == null || accessPolicyEntity.getComponent() == null) {
throw new IllegalArgumentException("Access policy details must be specified."); throw new IllegalArgumentException("Access policy details must be specified.");
} }
@ -425,6 +447,11 @@ public class AccessPolicyResource extends ApplicationResource {
) )
@PathParam("id") final String id) { @PathParam("id") final String id) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE); return replicate(HttpMethod.DELETE);
} }

View File

@ -300,6 +300,10 @@ public class FlowResource extends ApplicationResource {
authorizeFlow(); authorizeFlow();
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
final FlowConfigurationEntity entity = serviceFacade.getFlowConfiguration(); final FlowConfigurationEntity entity = serviceFacade.getFlowConfiguration();
return clusterContext(generateOkResponse(entity)).build(); return clusterContext(generateOkResponse(entity)).build();
} }
@ -321,6 +325,10 @@ public class FlowResource extends ApplicationResource {
authorizeFlow(); authorizeFlow();
if (isReplicateRequest()) {
return replicate(HttpMethod.GET);
}
// note that the cluster manager will handle this request directly // note that the cluster manager will handle this request directly
final NiFiUser user = NiFiUserUtils.getNiFiUser(); final NiFiUser user = NiFiUserUtils.getNiFiUser();
if (user == null) { if (user == null) {

View File

@ -23,6 +23,7 @@ import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses; import com.wordnik.swagger.annotations.ApiResponses;
import com.wordnik.swagger.annotations.Authorization; import com.wordnik.swagger.annotations.Authorization;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.RequestAction; import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.resource.Authorizable; import org.apache.nifi.authorization.resource.Authorizable;
@ -45,6 +46,7 @@ import org.apache.nifi.web.api.entity.UserGroupsEntity;
import org.apache.nifi.web.api.entity.UsersEntity; import org.apache.nifi.web.api.entity.UsersEntity;
import org.apache.nifi.web.api.request.ClientIdParameter; import org.apache.nifi.web.api.request.ClientIdParameter;
import org.apache.nifi.web.api.request.LongParameter; import org.apache.nifi.web.api.request.LongParameter;
import org.apache.nifi.web.dao.AccessPolicyDAO;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
@ -105,7 +107,7 @@ public class TenantsResource extends ApplicationResource {
* @return userEntity * @return userEntity
*/ */
public UserEntity populateRemainingUserEntityContent(UserEntity userEntity) { public UserEntity populateRemainingUserEntityContent(UserEntity userEntity) {
userEntity.setUri(generateResourceUri("tenants/users", userEntity.getId())); userEntity.setUri(generateResourceUri("tenants", "users", userEntity.getId()));
return userEntity; return userEntity;
} }
@ -144,6 +146,11 @@ public class TenantsResource extends ApplicationResource {
required = true required = true
) final UserEntity userEntity) { ) final UserEntity userEntity) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (userEntity == null || userEntity.getComponent() == null) { if (userEntity == null || userEntity.getComponent() == null) {
throw new IllegalArgumentException("User details must be specified."); throw new IllegalArgumentException("User details must be specified.");
} }
@ -224,6 +231,11 @@ public class TenantsResource extends ApplicationResource {
) )
@PathParam("id") final String id) { @PathParam("id") final String id) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.GET); return replicate(HttpMethod.GET);
} }
@ -271,6 +283,11 @@ public class TenantsResource extends ApplicationResource {
) )
public Response getUsers() { public Response getUsers() {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.GET); return replicate(HttpMethod.GET);
} }
@ -334,6 +351,11 @@ public class TenantsResource extends ApplicationResource {
required = true required = true
) final UserEntity userEntity) { ) final UserEntity userEntity) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (userEntity == null || userEntity.getComponent() == null) { if (userEntity == null || userEntity.getComponent() == null) {
throw new IllegalArgumentException("User details must be specified."); throw new IllegalArgumentException("User details must be specified.");
} }
@ -424,6 +446,11 @@ public class TenantsResource extends ApplicationResource {
) )
@PathParam("id") final String id) { @PathParam("id") final String id) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE); return replicate(HttpMethod.DELETE);
} }
@ -466,7 +493,7 @@ public class TenantsResource extends ApplicationResource {
* @return userGroupEntity * @return userGroupEntity
*/ */
public UserGroupEntity populateRemainingUserGroupEntityContent(UserGroupEntity userGroupEntity) { public UserGroupEntity populateRemainingUserGroupEntityContent(UserGroupEntity userGroupEntity) {
userGroupEntity.setUri(generateResourceUri("tenants/user-groups", userGroupEntity.getId())); userGroupEntity.setUri(generateResourceUri("tenants", "user-groups", userGroupEntity.getId()));
return userGroupEntity; return userGroupEntity;
} }
@ -505,6 +532,11 @@ public class TenantsResource extends ApplicationResource {
required = true required = true
) final UserGroupEntity userGroupEntity) { ) final UserGroupEntity userGroupEntity) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (userGroupEntity == null || userGroupEntity.getComponent() == null) { if (userGroupEntity == null || userGroupEntity.getComponent() == null) {
throw new IllegalArgumentException("User group details must be specified."); throw new IllegalArgumentException("User group details must be specified.");
} }
@ -585,6 +617,11 @@ public class TenantsResource extends ApplicationResource {
) )
@PathParam("id") final String id) { @PathParam("id") final String id) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.GET); return replicate(HttpMethod.GET);
} }
@ -632,6 +669,11 @@ public class TenantsResource extends ApplicationResource {
) )
public Response getUserGroups() { public Response getUserGroups() {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.GET); return replicate(HttpMethod.GET);
} }
@ -694,6 +736,11 @@ public class TenantsResource extends ApplicationResource {
required = true required = true
) final UserGroupEntity userGroupEntity) { ) final UserGroupEntity userGroupEntity) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (userGroupEntity == null || userGroupEntity.getComponent() == null) { if (userGroupEntity == null || userGroupEntity.getComponent() == null) {
throw new IllegalArgumentException("User group details must be specified."); throw new IllegalArgumentException("User group details must be specified.");
} }
@ -784,6 +831,11 @@ public class TenantsResource extends ApplicationResource {
) )
@PathParam("id") final String id) { @PathParam("id") final String id) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.DELETE); return replicate(HttpMethod.DELETE);
} }
@ -846,6 +898,11 @@ public class TenantsResource extends ApplicationResource {
) )
@QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) { @QueryParam("q") @DefaultValue(StringUtils.EMPTY) String value) {
// ensure we're running with a configurable authorizer
if (!(authorizer instanceof AbstractPolicyBasedAuthorizer)) {
throw new IllegalStateException(AccessPolicyDAO.MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER);
}
if (isReplicateRequest()) { if (isReplicateRequest()) {
return replicate(HttpMethod.GET); return replicate(HttpMethod.GET);
} }

View File

@ -34,6 +34,7 @@ import org.apache.nifi.action.details.PurgeDetails;
import org.apache.nifi.annotation.behavior.Stateful; import org.apache.nifi.annotation.behavior.Stateful;
import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags; import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
import org.apache.nifi.authorization.AccessPolicy; import org.apache.nifi.authorization.AccessPolicy;
import org.apache.nifi.authorization.Authorizer; import org.apache.nifi.authorization.Authorizer;
import org.apache.nifi.authorization.Group; import org.apache.nifi.authorization.Group;
@ -199,6 +200,7 @@ public final class DtoFactory {
// get the refresh interval // get the refresh interval
final long refreshInterval = FormatUtils.getTimeDuration(autoRefreshInterval, TimeUnit.SECONDS); final long refreshInterval = FormatUtils.getTimeDuration(autoRefreshInterval, TimeUnit.SECONDS);
dto.setAutoRefreshIntervalSeconds(refreshInterval); dto.setAutoRefreshIntervalSeconds(refreshInterval);
dto.setSupportsConfigurableAuthorizer(authorizer instanceof AbstractPolicyBasedAuthorizer);
final Date now = new Date(); final Date now = new Date();
dto.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime())); dto.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime()));

View File

@ -23,6 +23,8 @@ import org.apache.nifi.web.api.dto.AccessPolicyDTO;
public interface AccessPolicyDAO { public interface AccessPolicyDAO {
String MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER = "This NiFi is not configured to internally manage users, groups, and policies. Please contact your system administrator.";
/** /**
* Whether or not NiFi supports a configurable authorizer. * Whether or not NiFi supports a configurable authorizer.
* *

View File

@ -44,7 +44,6 @@ import java.util.stream.Collectors;
public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGroupDAO, UserDAO { public class StandardPolicyBasedAuthorizerDAO implements AccessPolicyDAO, UserGroupDAO, UserDAO {
static final String MSG_NON_ABSTRACT_POLICY_BASED_AUTHORIZER = "This NiFi is not configured to internally manage users, groups, and policies. Please contact your system administrator.";
private final AbstractPolicyBasedAuthorizer authorizer; private final AbstractPolicyBasedAuthorizer authorizer;
private final boolean supportsConfigurableAuthorizer; private final boolean supportsConfigurableAuthorizer;

View File

@ -147,15 +147,15 @@
<i class="fa fa-history"></i>Flow Configuration History <i class="fa fa-history"></i>Flow Configuration History
</a> </a>
</md-menu-item> </md-menu-item>
<md-menu-divider></md-menu-divider> <md-menu-divider ng-if="appCtrl.nf.Canvas.isConfigurableAuthorizer()"></md-menu-divider>
<md-menu-item layout-align="space-around center"> <md-menu-item layout-align="space-around center" ng-if="appCtrl.nf.Canvas.isConfigurableAuthorizer()">
<a id="users-link" layout="row" <a id="users-link" layout="row"
ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.users.shell.launch();" ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.users.shell.launch();"
ng-class="{disabled: !appCtrl.nf.Common.canAccessTenants()}"> ng-class="{disabled: !(appCtrl.nf.Common.canAccessTenants())}">
<i class="fa fa-users"></i>Users <i class="fa fa-users"></i>Users
</a> </a>
</md-menu-item> </md-menu-item>
<md-menu-item layout-align="space-around center"> <md-menu-item layout-align="space-around center" ng-if="appCtrl.nf.Canvas.isConfigurableAuthorizer()">
<a id="policies-link" layout="row" <a id="policies-link" layout="row"
ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.policies.shell.launch();" ng-click="appCtrl.serviceProvider.headerCtrl.globalMenuCtrl.policies.shell.launch();"
ng-class="{disabled: !(appCtrl.nf.Common.canAccessTenants() && appCtrl.nf.Common.canModifyPolicies())}"> ng-class="{disabled: !(appCtrl.nf.Common.canAccessTenants() && appCtrl.nf.Common.canModifyPolicies())}">

View File

@ -99,8 +99,8 @@
ng-disabled="!(appCtrl.serviceProvider.graphControlsCtrl.canConfigureOrOpenDetails())"> ng-disabled="!(appCtrl.serviceProvider.graphControlsCtrl.canConfigureOrOpenDetails())">
<div class="graph-control-action-icon fa fa-gear"></div></button> <div class="graph-control-action-icon fa fa-gear"></div></button>
</div> </div>
<div class="button-spacer-small">&nbsp;</div> <div class="button-spacer-small" ng-if="appCtrl.nf.Canvas.isConfigurableAuthorizer()">&nbsp;</div>
<div id="operate-policy" class="action-button" title="Access Policies"> <div id="operate-policy" class="action-button" title="Access Policies" ng-if="appCtrl.nf.Canvas.isConfigurableAuthorizer()">
<button ng-click="appCtrl.nf.Actions['managePolicies'](appCtrl.nf.CanvasUtils.getSelection());" <button ng-click="appCtrl.nf.Actions['managePolicies'](appCtrl.nf.CanvasUtils.getSelection());"
ng-disabled="!(appCtrl.serviceProvider.graphControlsCtrl.canManagePolicies())"> ng-disabled="!(appCtrl.serviceProvider.graphControlsCtrl.canManagePolicies())">
<div class="graph-control-action-icon fa fa-key"></div></button> <div class="graph-control-action-icon fa fa-key"></div></button>

View File

@ -133,7 +133,7 @@ div.policy-selected-component-name {
color: #262626; color: #262626;
width: 300px; width: 300px;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow-x: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
} }

View File

@ -118,6 +118,7 @@ nf.Canvas = (function () {
var parentGroupId = null; var parentGroupId = null;
var clustered = false; var clustered = false;
var connectedToCluster = false; var connectedToCluster = false;
var configurableAuthorizer = false;
var svg = null; var svg = null;
var canvas = null; var canvas = null;
@ -884,6 +885,9 @@ nf.Canvas = (function () {
// get the auto refresh interval // get the auto refresh interval
var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10); var autoRefreshIntervalSeconds = parseInt(configDetails.autoRefreshIntervalSeconds, 10);
// record whether we can configure the authorizer
configurableAuthorizer = configDetails.supportsConfigurableAuthorizer;
// init storage // init storage
nf.Storage.init(); nf.Storage.init();
@ -1008,6 +1012,13 @@ nf.Canvas = (function () {
return parentGroupId; return parentGroupId;
}, },
/**
* Returns whether the authorizer is configurable.
*/
isConfigurableAuthorizer: function () {
return configurableAuthorizer;
},
/** /**
* Whether the current user can write in this group. * Whether the current user can write in this group.
* *

View File

@ -605,8 +605,10 @@ nf.ControllerServices = (function () {
} }
} }
// TODO - only if we can adminster policies // allow policy configuration conditionally
if (nf.Canvas.isConfigurableAuthorizer() && nf.Common.canAccessTenants()) {
markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>'; markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>';
}
return markup; return markup;
}; };

View File

@ -682,8 +682,10 @@ nf.Settings = (function () {
} }
} }
// TODO - only if we can adminster policies // allow policy configuration conditionally
if (nf.Canvas.isConfigurableAuthorizer() && nf.Common.canAccessTenants()) {
markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>'; markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>';
}
return markup; return markup;
}; };

View File

@ -283,11 +283,12 @@ nf.TemplatesTable = (function () {
markup += '<div title="Remove Template" class="pointer prompt-to-delete-template fa fa-trash" style="margin-top: 2px; margin-right: 3px;"></div>'; markup += '<div title="Remove Template" class="pointer prompt-to-delete-template fa fa-trash" style="margin-top: 2px; margin-right: 3px;"></div>';
} }
// if we in the shell // allow policy configuration conditionally
// TODO - only if we can adminster policies if (top !== window && nf.Common.canAccessTenants()) {
if (top !== window) { if (nf.Common.isDefinedAndNotNull(parent.nf) && nf.Common.isDefinedAndNotNull(parent.nf.Canvas) && parent.nf.Canvas.isConfigurableAuthorizer()) {
markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>'; markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key" style="margin-top: 2px;"></div>';
} }
}
return markup; return markup;
}; };