提交 b853537b 编写于 作者: T Therese Magnusson 提交者: GitHub

Grant, deny, revoke idempotency and notifications ()

Co-authored-by: NLasse Heemann <lasse.heemann@neo4j.com>
上级 84d77a86
......@@ -134,8 +134,10 @@ org.neo4j.kernel.api.exceptions.Status.Security::AuthProviderFailed org.neo4j.ke
org.neo4j.kernel.api.exceptions.Status.Security::AuthProviderTimeout org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::AuthenticationRateLimit org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::AuthorizationExpired org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::CommandHasNoEffect org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::CredentialsExpired org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::Forbidden org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::ImpossibleRevokeCommand org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::ModifiedConcurrently org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::TokenExpired org.neo4j.kernel.api.exceptions.Status.Security public static final
org.neo4j.kernel.api.exceptions.Status.Security::Unauthorized org.neo4j.kernel.api.exceptions.Status.Security public static final
......
......@@ -513,7 +513,13 @@ public interface Status {
AuthorizationExpired(ClientError, "The stored authorization info has expired. Please reconnect."),
AuthProviderTimeout(TransientError, "An auth provider request timed out."),
AuthProviderFailed(TransientError, "An auth provider request failed."),
TokenExpired(ClientError, "The auth provider token has expired");
TokenExpired(ClientError, "The auth provider token has expired"),
// Administration command
CommandHasNoEffect(
ClientNotification, "`%s` has no effect.", SeverityLevel.INFORMATION, NotificationCategory.SECURITY),
ImpossibleRevokeCommand(
ClientNotification, "`%s` has no effect.", SeverityLevel.WARNING, NotificationCategory.SECURITY);
private final Code code;
......@@ -525,6 +531,14 @@ public interface Status {
Security(Classification classification, String description) {
this.code = new Code(classification, this, description);
}
Security(
Classification classification,
String description,
SeverityLevel severity,
NotificationCategory category) {
this.code = new NotificationCode(classification, this, description, severity, category);
}
}
enum General implements Status {
......
......@@ -141,13 +141,15 @@ idGen: IdGen) extends SecurityAdministrationLogicalPlan(Some(source))
case class GrantRoleToUser(
source: SecurityAdministrationLogicalPlan,
roleName: Either[String, Parameter],
userName: Either[String, Parameter]
userName: Either[String, Parameter],
command: String
)(implicit idGen: IdGen) extends SecurityAdministrationLogicalPlan(Some(source))
case class RevokeRoleFromUser(
source: SecurityAdministrationLogicalPlan,
roleName: Either[String, Parameter],
userName: Either[String, Parameter]
userName: Either[String, Parameter],
command: String
)(implicit idGen: IdGen) extends SecurityAdministrationLogicalPlan(Some(source))
case class RequireRole(source: SecurityAdministrationLogicalPlan, name: Either[String, Parameter])(implicit
......@@ -219,7 +221,8 @@ case class GrantDbmsAction(
action: DbmsAction,
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
immutable: Boolean
immutable: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class DenyDbmsAction(
......@@ -227,7 +230,8 @@ case class DenyDbmsAction(
action: DbmsAction,
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
immutable: Boolean
immutable: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class RevokeDbmsAction(
......@@ -236,7 +240,8 @@ case class RevokeDbmsAction(
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
revokeType: String,
immutableOnly: Boolean
immutableOnly: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class AssertDbmsPrivilegeCanBeMutated(
......@@ -253,7 +258,8 @@ case class GrantDatabaseAction(
database: DatabaseScope,
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
immutable: Boolean
immutable: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class DenyDatabaseAction(
......@@ -262,7 +268,8 @@ case class DenyDatabaseAction(
database: DatabaseScope,
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
immutable: Boolean
immutable: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class RevokeDatabaseAction(
......@@ -272,7 +279,8 @@ case class RevokeDatabaseAction(
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
revokeType: String,
immutableOnly: Boolean
immutableOnly: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class AssertDatabasePrivilegeCanBeMutated(
......@@ -291,7 +299,8 @@ case class GrantGraphAction(
graph: GraphScope,
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
immutable: Boolean
immutable: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class DenyGraphAction(
......@@ -301,7 +310,8 @@ case class DenyGraphAction(
graph: GraphScope,
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
immutable: Boolean
immutable: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class RevokeGraphAction(
......@@ -312,7 +322,8 @@ case class RevokeGraphAction(
qualifier: PrivilegeQualifier,
roleName: Either[String, Parameter],
revokeType: String,
immutableOnly: Boolean
immutableOnly: Boolean,
command: String
)(implicit idGen: IdGen) extends PrivilegePlan(Some(source))
case class AssertGraphPrivilegeCanBeMutated(
......
......@@ -173,12 +173,12 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
def planRevokes(
source: plans.PrivilegePlan,
revokeType: RevokeType,
planRevoke: (plans.PrivilegePlan, String) => plans.PrivilegePlan
planRevoke: (plans.PrivilegePlan, RevokeType) => plans.PrivilegePlan
): plans.PrivilegePlan = revokeType match {
case t: RevokeBothType =>
val revokeGrant = planRevoke(source, RevokeGrantType()(t.position).relType)
planRevoke(revokeGrant, RevokeDenyType()(t.position).relType)
case t => planRevoke(source, t.relType)
val revokeGrant = planRevoke(source, RevokeGrantType()(t.position))
planRevoke(revokeGrant, RevokeDenyType()(t.position))
case t => planRevoke(source, t)
}
def getSourceForCreateRole(
......@@ -388,7 +388,12 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
}).foldLeft(
plans.AssertAllowedDbmsActions(AssignRoleAction).asInstanceOf[plans.SecurityAdministrationLogicalPlan]
) {
case (source, (roleName, userName)) => plans.GrantRoleToUser(source, roleName, userName)
case (source, (roleName, userName)) =>
val subCommand = c.copy(
roleNames = List(roleName),
userNames = List(userName)
)(c.position)
plans.GrantRoleToUser(source, roleName, userName, prettifier.asString(subCommand))
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
......@@ -399,7 +404,12 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
}).foldLeft(
plans.AssertAllowedDbmsActions(RemoveRoleAction).asInstanceOf[plans.SecurityAdministrationLogicalPlan]
) {
case (source, (roleName, userName)) => plans.RevokeRoleFromUser(source, roleName, userName)
case (source, (roleName, userName)) =>
val subCommand = c.copy(
roleNames = List(roleName),
userNames = List(userName)
)(c.position)
plans.RevokeRoleFromUser(source, roleName, userName, prettifier.asString(subCommand))
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
......@@ -414,7 +424,11 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
).asInstanceOf[plans.PrivilegePlan]
) {
case (source, (roleName, simpleQualifier)) =>
plans.GrantDbmsAction(source, action, simpleQualifier, roleName, immutable)
val subCommand = c.copy(
qualifier = List(simpleQualifier),
roleNames = List(roleName)
)(c.position)
plans.GrantDbmsAction(source, action, simpleQualifier, roleName, immutable, prettifier.asString(subCommand))
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
......@@ -429,7 +443,11 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
).asInstanceOf[plans.PrivilegePlan]
) {
case (source, (roleName, simpleQualifier)) =>
plans.DenyDbmsAction(source, action, simpleQualifier, roleName, immutable)
val subCommand = c.copy(
qualifier = List(simpleQualifier),
roleNames = List(roleName)
)(c.position)
plans.DenyDbmsAction(source, action, simpleQualifier, roleName, immutable, prettifier.asString(subCommand))
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
......@@ -447,25 +465,29 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
planRevokes(
previous,
revokeType,
(s, r) =>
(s, r) => {
val subCommand =
c.copy(qualifier = List(simpleQualifier), roleNames = List(roleName), revokeType = r)(c.position)
plans.RevokeDbmsAction(
planRevokes(
s,
revokeType,
(s, r) => plans.AssertDbmsPrivilegeCanBeMutated(s, action, simpleQualifier, roleName, r)
(s, r) => plans.AssertDbmsPrivilegeCanBeMutated(s, action, simpleQualifier, roleName, r.relType)
),
action,
simpleQualifier,
roleName,
r,
immutableOnly
r.relType,
immutableOnly,
prettifier.asString(subCommand)
)
}
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
// GRANT _ ON DATABASE foo TO role
case c @ GrantPrivilege(DatabasePrivilege(action, dbScopes), immutable, _, qualifiers, roleNames) =>
case c @ GrantPrivilege(privilege @ DatabasePrivilege(action, dbScopes), immutable, _, qualifiers, roleNames) =>
val plan = (for (
dbScope <- dbScopes; roleName <- roleNames; qualifier <- qualifiers; simpleQualifiers <- qualifier.simplify
) yield {
......@@ -474,12 +496,25 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
plans.AssertAllowedDbmsActions(assignPrivilegeAction(immutable)).asInstanceOf[plans.PrivilegePlan]
) {
case (source, (role, qualifier, dbScope: DatabaseScope)) =>
plans.GrantDatabaseAction(source, action, dbScope, qualifier, role, immutable)
val subCommand = c.copy(
privilege = privilege.copy(scope = List(dbScope))(privilege.position),
qualifier = List(qualifier),
roleNames = List(role)
)(c.position)
plans.GrantDatabaseAction(
source,
action,
dbScope,
qualifier,
role,
immutable,
prettifier.asString(subCommand)
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
// DENY _ ON DATABASE foo TO role
case c @ DenyPrivilege(DatabasePrivilege(action, dbScopes), immutable, _, qualifiers, roleNames) =>
case c @ DenyPrivilege(privilege @ DatabasePrivilege(action, dbScopes), immutable, _, qualifiers, roleNames) =>
val plan = (for (
dbScope <- dbScopes; roleName <- roleNames; qualifier <- qualifiers; simpleQualifiers <- qualifier.simplify
) yield {
......@@ -488,13 +523,26 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
plans.AssertAllowedDbmsActions(assignPrivilegeAction(immutable)).asInstanceOf[plans.PrivilegePlan]
) {
case (source, (role, qualifier, dbScope: DatabaseScope)) =>
plans.DenyDatabaseAction(source, action, dbScope, qualifier, role, immutable)
val subCommand = c.copy(
privilege = privilege.copy(scope = List(dbScope))(privilege.position),
qualifier = List(qualifier),
roleNames = List(role)
)(c.position)
plans.DenyDatabaseAction(
source,
action,
dbScope,
qualifier,
role,
immutable,
prettifier.asString(subCommand)
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
// REVOKE _ ON DATABASE foo FROM role
case c @ RevokePrivilege(
DatabasePrivilege(action, dbScopes),
privilege @ DatabasePrivilege(action, dbScopes),
immutableOnly,
_,
qualifiers,
......@@ -510,27 +558,35 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
planRevokes(
plan,
revokeType,
(s, r) =>
(s, r) => {
val subCommand = c.copy(
privilege = privilege.copy(scope = List(dbScope))(privilege.position),
qualifier = List(qualifier),
roleNames = List(role),
revokeType = r
)(c.position)
plans.RevokeDatabaseAction(
planRevokes(
s,
revokeType,
(s, r) => plans.AssertDatabasePrivilegeCanBeMutated(s, action, dbScope, qualifier, role, r)
(s, r) => plans.AssertDatabasePrivilegeCanBeMutated(s, action, dbScope, qualifier, role, r.relType)
),
action,
dbScope,
qualifier,
role,
r,
immutableOnly
r.relType,
immutableOnly,
prettifier.asString(subCommand)
)
}
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
// GRANT _ ON GRAPH foo _ TO role
case c @ GrantPrivilege(
GraphPrivilege(action, graphScopes),
privilege @ GraphPrivilege(action, graphScopes),
immutable,
optionalResource,
qualifiers,
......@@ -539,36 +595,70 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
val resources = optionalResource.getOrElse(NoResource()(InputPosition.NONE))
val plan = (for (
graphScope <- graphScopes; roleName <- roleNames; qualifier <- qualifiers;
simpleQualifiers <- qualifier.simplify; resource <- resources.simplify
simpleQualifier <- qualifier.simplify; resource <- resources.simplify
) yield {
(roleName, simpleQualifiers, resource, graphScope)
(roleName, simpleQualifier, resource, graphScope)
}).foldLeft(
plans.AssertAllowedDbmsActions(assignPrivilegeAction(immutable)).asInstanceOf[plans.PrivilegePlan]
) {
case (source, (roleName, segment, resource, graphScope: GraphScope)) =>
plans.GrantGraphAction(source, action, resource, graphScope, segment, roleName, immutable)
case (source, (roleName, simpleQualifier, resource, graphScope: GraphScope)) =>
val subCommand = c.copy(
privilege = privilege.copy(scope = List(graphScope))(privilege.position),
qualifier = List(simpleQualifier),
roleNames = List(roleName)
)(c.position)
plans.GrantGraphAction(
source,
action,
resource,
graphScope,
simpleQualifier,
roleName,
immutable,
prettifier.asString(subCommand)
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
// DENY _ ON GRAPH foo _ TO role
case c @ DenyPrivilege(GraphPrivilege(action, graphScopes), immutable, optionalResource, qualifiers, roleNames) =>
case c @ DenyPrivilege(
privilege @ GraphPrivilege(action, graphScopes),
immutable,
optionalResource,
qualifiers,
roleNames
) =>
val resources = optionalResource.getOrElse(NoResource()(InputPosition.NONE))
val plan = (for (
graphScope <- graphScopes; roleName <- roleNames; qualifier <- qualifiers;
simpleQualifiers <- qualifier.simplify; resource <- resources.simplify
simpleQualifier <- qualifier.simplify; resource <- resources.simplify
) yield {
(roleName, simpleQualifiers, resource, graphScope)
(roleName, simpleQualifier, resource, graphScope)
}).foldLeft(
plans.AssertAllowedDbmsActions(assignPrivilegeAction(immutable)).asInstanceOf[plans.PrivilegePlan]
) {
case (source, (roleName, segment, resource, graphScope: GraphScope)) =>
plans.DenyGraphAction(source, action, resource, graphScope, segment, roleName, immutable)
case (source, (roleName, simpleQualifier, resource, graphScope: GraphScope)) =>
val subCommand = c.copy(
privilege = privilege.copy(scope = List(graphScope))(privilege.position),
qualifier = List(simpleQualifier),
roleNames = List(roleName)
)(c.position)
plans.DenyGraphAction(
source,
action,
resource,
graphScope,
simpleQualifier,
roleName,
immutable,
prettifier.asString(subCommand)
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
// REVOKE _ ON GRAPH foo _ FROM role
case c @ RevokePrivilege(
GraphPrivilege(action, graphScopes),
privilege @ GraphPrivilege(action, graphScopes),
immutableOnly,
optionalResource,
qualifiers,
......@@ -586,22 +676,39 @@ case object AdministrationCommandPlanBuilder extends Phase[PlannerContext, BaseS
planRevokes(
source,
revokeType,
(s, r) =>
(s, r) => {
val subCommand = c.copy(
privilege = privilege.copy(scope = List(graphScope))(privilege.position),
qualifier = List(segment),
resource = if (resource.isInstanceOf[NoResource]) None else Some(resource),
roleNames = List(roleName),
revokeType = r
)(c.position)
plans.RevokeGraphAction(
planRevokes(
s,
revokeType,
(s, r) =>
plans.AssertGraphPrivilegeCanBeMutated(s, action, resource, graphScope, segment, roleName, r)
plans.AssertGraphPrivilegeCanBeMutated(
s,
action,
resource,
graphScope,
segment,
roleName,
r.relType
)
),
action,
resource,
graphScope,
segment,
roleName,
r,
immutableOnly
r.relType,
immutableOnly,
prettifier.asString(subCommand)
)
}
)
}
Some(plans.LogSystemCommand(plan, prettifier.asString(c)))
......
......@@ -259,11 +259,11 @@ object AdministrationCommandRuntime {
),
QueryHandler
.handleNoResult(params =>
Some(
Some(ThrowException(
new CypherExecutionException(
s"Failed to create the specified user '${runtimeStringValue(userName, params)}'."
)
)
))
)
.handleError((error, params) =>
(error, error.getCause) match {
......@@ -361,9 +361,9 @@ object AdministrationCommandRuntime {
VirtualValues.map(parameterKeys.toArray, parameterValues.toArray),
QueryHandler
.handleNoResult(p =>
Some(new InvalidArgumentException(
Some(ThrowException(new InvalidArgumentException(
s"Failed to alter the specified user '${runtimeStringValue(userName, p)}': User does not exist."
))
)))
)
.handleError {
case (error: HasStatus, p) if error.status() == Status.Cluster.NotALeader =>
......@@ -445,10 +445,10 @@ object AdministrationCommandRuntime {
),
QueryHandler
.handleNoResult(p =>
Some(new InvalidArgumentException(
Some(ThrowException(new InvalidArgumentException(
s"Failed to rename the specified ${entity.toLowerCase(Locale.ROOT)} '${runtimeStringValue(fromName, p)}' to " +
s"'${runtimeStringValue(toName, p)}': The ${entity.toLowerCase(Locale.ROOT)} '${runtimeStringValue(fromName, p)}' does not exist."
))
)))
)
.handleError((error, p) =>
(error, error.getCause) match {
......
......@@ -41,6 +41,7 @@ import org.neo4j.cypher.internal.compiler.NodeIndexLookupUnfulfillableNotificati
import org.neo4j.cypher.internal.compiler.ProcedureWarningNotification
import org.neo4j.cypher.internal.compiler.RelationshipIndexLookupUnfulfillableNotification
import org.neo4j.cypher.internal.compiler.RuntimeUnsupportedNotification
import org.neo4j.cypher.internal.util.AssignPrivilegeCommandHasNoEffectNotification
import org.neo4j.cypher.internal.util.CartesianProductNotification
import org.neo4j.cypher.internal.util.DeprecatedConnectComponentsPlannerPreParserOption
import org.neo4j.cypher.internal.util.DeprecatedDatabaseNameNotification
......@@ -50,11 +51,15 @@ import org.neo4j.cypher.internal.util.DeprecatedRelTypeSeparatorNotification
import org.neo4j.cypher.internal.util.DeprecatedRuntimeNotification
import org.neo4j.cypher.internal.util.DeprecatedTextIndexProvider
import org.neo4j.cypher.internal.util.FixedLengthRelationshipInShortestPath
import org.neo4j.cypher.internal.util.GrantRoleCommandHasNoEffectNotification
import org.neo4j.cypher.internal.util.HomeDatabaseNotPresent
import org.neo4j.cypher.internal.util.ImpossibleRevokeCommandWarning
import org.neo4j.cypher.internal.util.InputPosition
import org.neo4j.cypher.internal.util.InternalNotification
import org.neo4j.cypher.internal.util.RepeatedRelationshipReference
import org.neo4j.cypher.internal.util.RepeatedVarLengthRelationshipReference
import org.neo4j.cypher.internal.util.RevokePrivilegeCommandHasNoEffectNotification
import org.neo4j.cypher.internal.util.RevokeRoleCommandHasNoEffectNotification
import org.neo4j.cypher.internal.util.SubqueryVariableShadowing
import org.neo4j.cypher.internal.util.UnboundedShortestPathNotification
import org.neo4j.cypher.internal.util.UnionReturnItemsInDifferentOrder
......@@ -243,6 +248,41 @@ object NotificationWrapping {
position.asInputPosition
)
case AssignPrivilegeCommandHasNoEffectNotification(command) =>
NotificationCodeWithDescription.COMMAND_HAS_NO_EFFECT.notificationWithTitleAndDescriptionDetails(
graphdb.InputPosition.empty,
command,
"The role already has the privilege."
)
case RevokePrivilegeCommandHasNoEffectNotification(command) =>
NotificationCodeWithDescription.COMMAND_HAS_NO_EFFECT.notificationWithTitleAndDescriptionDetails(
graphdb.InputPosition.empty,
command,
"The role does not have the privilege."
)
case GrantRoleCommandHasNoEffectNotification(command) =>
NotificationCodeWithDescription.COMMAND_HAS_NO_EFFECT.notificationWithTitleAndDescriptionDetails(
graphdb.InputPosition.empty,
command,
"The user already has the role."
)
case RevokeRoleCommandHasNoEffectNotification(command) =>
NotificationCodeWithDescription.COMMAND_HAS_NO_EFFECT.notificationWithTitleAndDescriptionDetails(
graphdb.InputPosition.empty,
command,
"The user does not have the role."
)
case ImpossibleRevokeCommandWarning(command, cause) =>
NotificationCodeWithDescription.IMPOSSIBLE_REVOKE_COMMAND.notificationWithTitleAndDescriptionDetails(
graphdb.InputPosition.empty,
command,
cause
)
case _ => throw new IllegalStateException("Missing mapping for notification detail.")
}
......
......@@ -32,6 +32,7 @@ import org.neo4j.cypher.internal.ast.DatabaseName
import org.neo4j.cypher.internal.expressions.Parameter
import org.neo4j.cypher.internal.procs.ParameterTransformer
import org.neo4j.cypher.internal.procs.QueryHandler
import org.neo4j.cypher.internal.procs.ThrowException
import org.neo4j.cypher.internal.procs.UpdatingSystemCommandExecutionPlan
import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel.DATABASE_NAME
import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel.DATABASE_NAME_LABEL_DESCRIPTION
......@@ -100,9 +101,9 @@ case class EnsureNodeExistsExecutionPlanner(
private def queryHandler[T](action: String, labelDescription: String, value: T)(implicit show: Show[T]) = {
QueryHandler
.handleNoResult(p =>
Some(new InvalidArgumentException(
Some(ThrowException(new InvalidArgumentException(
s"Failed to $action the specified ${labelDescription.toLowerCase} '${show(value, p)}': $labelDescription does not exist."
))
)))
)
.handleError {
case (error: HasStatus, p) if error.status() == Status.Cluster.NotALeader =>
......
......@@ -111,13 +111,13 @@ case class SetOwnPasswordExecutionPlanner(
if (
currentUser(p).isEmpty
) // This is true if the securityContext is AUTH_DISABLED (both for community and enterprise)
Some(new IllegalStateException(
Some(ThrowException(new IllegalStateException(
"User failed to alter their own password: Command not available with auth disabled."
))
)))
else // The 'current user' doesn't exist in the system graph
Some(new IllegalStateException(
Some(ThrowException(new IllegalStateException(
s"User '${currentUser(p)}' failed to alter their own password: User does not exist."
))
)))
}),
checkCredentialsExpired = false,
initAndFinally = InitAndFinallyFunctions(finallyFunction = p => {
......
......@@ -92,7 +92,7 @@ case class SystemCommandExecutionPlan(
systemSubscriber,
elevatedSecurityContext,
tc.kernelTransaction(),
previousNotifications ++ notifications
previousNotifications ++ notifications ++ systemSubscriber.getNotifications
)
}
}
......@@ -126,6 +126,7 @@ class SystemCommandQuerySubscriber(
@volatile private var ignore = false
@volatile private var failed: Option[Throwable] = None
@volatile private var contextUpdates: MapValue = MapValue.EMPTY
@volatile private var notifications: Set[InternalNotification] = Set.empty
override def onResult(numberOfFields: Int): Unit = if (failed.isEmpty) {
inner.onResult(numberOfFields)
......@@ -139,8 +140,9 @@ class SystemCommandQuerySubscriber(
failed = Some(error)
case IgnoreResults =>
ignore = true
case UpdateContextParams(params) => contextUpdates = contextUpdates.updatedWith(params)
case Continue => ()
case UpdateContextParams(params) => contextUpdates = contextUpdates.updatedWith(params)
case NotifyAndContinue(newNotifications) => notifications = notifications ++ newNotifications
case Continue => ()
}
}
if (failed.isEmpty) {
......@@ -169,8 +171,9 @@ class SystemCommandQuerySubscriber(
failed = Some(error)
case IgnoreResults =>
ignore = true
case UpdateContextParams(params) => contextUpdates = contextUpdates.updatedWith(params)
case Continue => ()
case UpdateContextParams(params) => contextUpdates = contextUpdates.updatedWith(params)
case NotifyAndContinue(newNotifications) => notifications = notifications ++ newNotifications
case Continue => ()
}
if (failed.isEmpty) {
inner.onField(offset, value)
......@@ -192,5 +195,7 @@ class SystemCommandQuerySubscriber(
def getContextUpdates: MapValue = contextUpdates
def getNotifications: Set[InternalNotification] = notifications
override def equals(obj: Any): Boolean = inner.equals(obj)
}
......@@ -102,11 +102,11 @@ case class UpdatingSystemCommandExecutionPlan(
systemSubscriber.assertNotFailed()
if (systemSubscriber.shouldIgnoreResult()) {
IgnoredRuntimeResult(previousNotifications ++ notifications)
IgnoredRuntimeResult(previousNotifications ++ notifications ++ systemSubscriber.getNotifications)
} else {
UpdatingSystemCommandRuntimeResult(
ctx.withContextVars(systemSubscriber.getContextUpdates),
previousNotifications ++ notifications
previousNotifications ++ notifications ++ systemSubscriber.getNotifications
)
}
}
......@@ -147,6 +147,7 @@ case object Continue extends QueryHandlerResult
case object IgnoreResults extends QueryHandlerResult
case class ThrowException(throwable: Throwable) extends QueryHandlerResult
case class UpdateContextParams(params: MapValue) extends QueryHandlerResult
case class NotifyAndContinue(notifications: Set[InternalNotification]) extends QueryHandlerResult
class QueryHandler {
def onError(t: Throwable, p: MapValue): Throwable = t
......@@ -176,10 +177,10 @@ class QueryHandlerBuilder(parent: QueryHandler) extends QueryHandler {
}
}
def handleNoResult(f: MapValue => Option[Throwable]): QueryHandlerBuilder = new QueryHandlerBuilder(this) {
def handleNoResult(f: MapValue => Option[QueryHandlerResult]): QueryHandlerBuilder = new QueryHandlerBuilder(this) {
override def onNoResults(params: MapValue): QueryHandlerResult =
f(params).map(t => ThrowException(t)).getOrElse(Continue)
f(params).getOrElse(Continue)
}
def ignoreNoResult(): QueryHandlerBuilder = new QueryHandlerBuilder(this) {
......@@ -204,7 +205,7 @@ object QueryHandler {
def handleError(f: (Throwable, MapValue) => Throwable): QueryHandlerBuilder =
new QueryHandlerBuilder(new QueryHandler).handleError(f)
def handleNoResult(f: MapValue => Option[Throwable]): QueryHandlerBuilder =
def handleNoResult(f: MapValue => Option[QueryHandlerResult]): QueryHandlerBuilder =
new QueryHandlerBuilder(new QueryHandler).handleNoResult(f)
def ignoreNoResult(): QueryHandlerBuilder = new QueryHandlerBuilder(new QueryHandler).ignoreNoResult()
......
......@@ -153,7 +153,7 @@ case class WaitReconciliationExecutionPlan(
systemSubscriber,
fullAccess,
tc.kernelTransaction(),
previousNotifications ++ notifications
previousNotifications ++ notifications ++ systemSubscriber.getNotifications
)
} finally {
if (revertAccessModeChange != null) revertAccessModeChange.close()
......
......@@ -5541,9 +5541,15 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
adminPlanDescription
)
assertGood(attach(GrantRoleToUser(privLhsLP, util.Left("role"), util.Left("user")), 1.0), adminPlanDescription)
assertGood(
attach(GrantRoleToUser(privLhsLP, util.Left("role"), util.Left("user"), "GRANT ROLE role TO user"), 1.0),
adminPlanDescription
)
assertGood(attach(RevokeRoleFromUser(privLhsLP, util.Left("role"), util.Left("user")), 1.0), adminPlanDescription)
assertGood(
attach(RevokeRoleFromUser(privLhsLP, util.Left("role"), util.Left("user"), "REVOKE ROLE role FROM user"), 1.0),
adminPlanDescription
)
assertGood(
attach(
......@@ -5552,7 +5558,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
ExecuteProcedureAction,
ProcedureAllQualifier()(pos),
util.Left("role1"),
immutable = false
immutable = false,
"GRANT ..."
),
1.0
),
......@@ -5566,7 +5573,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
ExecuteBoostedProcedureAction,
ProcedureQualifier("apoc.sin")(pos),
util.Left("role1"),
immutable = false
immutable = false,
"DENY ..."
),
1.0
),
......@@ -5581,7 +5589,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
ProcedureAllQualifier()(pos),
util.Left("role1"),
"GRANTED",
immutableOnly = false
immutableOnly = false,
"REVOKE GRANT ..."
),
1.0
),
......@@ -5596,7 +5605,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
NamedDatabaseScope(NamespacedName("foo")(pos))(pos),
UserAllQualifier()(pos),
util.Left("role1"),
immutable = false
immutable = false,
"GRANT ..."
),
1.0
),
......@@ -5611,7 +5621,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
AllDatabasesScope()(pos),
UserQualifier(util.Left("user1"))(pos),
util.Left("role1"),
immutable = false
immutable = false,
"DENY ..."
),
1.0
),
......@@ -5627,7 +5638,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
UserQualifier(util.Left("user1"))(pos),
util.Left("role1"),
"GRANTED",
immutableOnly = false
immutableOnly = false,
"REVOKE GRANT ..."
),
1.0
),
......@@ -5643,7 +5655,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
DefaultGraphScope()(pos),
LabelQualifier("Label1")(pos),
util.Left("role1"),
immutable = false
immutable = false,
"GRANT ..."
),
1.0
),
......@@ -5659,7 +5672,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
DefaultGraphScope()(pos),
LabelQualifier("Label1")(pos),
util.Left("role1"),
immutable = false
immutable = false,
"DENY ..."
),
1.0
),
......@@ -5676,7 +5690,8 @@ class LogicalPlan2PlanDescriptionTest extends CypherFunSuite with TableDrivenPro
ElementsAllQualifier()(pos),
util.Left("role1"),
"GRANTED",
immutableOnly = false
immutableOnly = false,
"REVOKE GRANT ..."
),
1.0
),
......
......@@ -105,6 +105,7 @@ import org.neo4j.cypher.internal.ast.IfExistsReplace
import org.neo4j.cypher.internal.ast.IfExistsThrowError
import org.neo4j.cypher.internal.ast.LabelAllQualifier
import org.neo4j.cypher.internal.ast.LabelQualifier
import org.neo4j.cypher.internal.ast.LabelResource
import org.neo4j.cypher.internal.ast.LabelsResource
import org.neo4j.cypher.internal.ast.Limit
import org.neo4j.cypher.internal.ast.LoadCSV
......@@ -1360,6 +1361,7 @@ object Prettifier {
case Some(PropertyResource(name)) => s" {${ExpressionStringifier.backtick(name)}}"
case Some(PropertiesResource(names)) => s" {${names.map(ExpressionStringifier.backtick(_)).mkString(", ")}}"
case Some(AllPropertyResource()) => " {*}"
case Some(LabelResource(name)) => s" ${ExpressionStringifier.backtick(name)}"
case Some(LabelsResource(names)) => s" ${names.map(ExpressionStringifier.backtick(_)).mkString(", ")}"
case Some(AllLabelResource()) => " *"
case None => ""
......
......@@ -58,3 +58,10 @@ case class RepeatedRelationshipReference(position: InputPosition, relName: Strin
case class RepeatedVarLengthRelationshipReference(position: InputPosition, relName: String) extends InternalNotification
case class DeprecatedConnectComponentsPlannerPreParserOption(position: InputPosition) extends InternalNotification
case class AssignPrivilegeCommandHasNoEffectNotification(command: String) extends InternalNotification
case class RevokePrivilegeCommandHasNoEffectNotification(command: String) extends InternalNotification
case class GrantRoleCommandHasNoEffectNotification(command: String) extends InternalNotification
case class RevokeRoleCommandHasNoEffectNotification(command: String) extends InternalNotification
case class ImpossibleRevokeCommandWarning(command: String, cause: String) extends InternalNotification
......@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.CARTESIAN_PRODUCT;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.CODE_GENERATION_FAILED;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.COMMAND_HAS_NO_EFFECT;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.DEPRECATED_CONNECT_COMPONENTS_PLANNER_PRE_PARSER_OPTION;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.DEPRECATED_DATABASE_NAME;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.DEPRECATED_FORMAT;
......@@ -36,6 +37,7 @@ import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescriptio
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.EAGER_LOAD_CSV;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.EXHAUSTIVE_SHORTEST_PATH;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.HOME_DATABASE_NOT_PRESENT;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.IMPOSSIBLE_REVOKE_COMMAND;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.INDEX_HINT_UNFULFILLABLE;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.INDEX_LOOKUP_FOR_DYNAMIC_PROPERTY;
import static org.neo4j.graphdb.impl.notification.NotificationCodeWithDescription.JOIN_HINT_UNFULFILLABLE;
......@@ -598,6 +600,33 @@ class NotificationCodeWithDescriptionTest {
NotificationCategory.DEPRECATION);
}
@Test
void shouldConstructNotificationsFor_COMMAND_HAD_NO_EFFECT() {
Notification notification = COMMAND_HAS_NO_EFFECT.notification(InputPosition.empty);
verifyNotification(
notification,
"`%s` has no effect.",
SeverityLevel.INFORMATION,
"Neo.ClientNotification.Security.CommandHasNoEffect",
"%s See Status Codes documentation for more information.",
NotificationCategory.SECURITY);
}
@Test
void shouldConstructNotificationsFor_IMPOSSIBLE_REVOKE_COMMAND() {
Notification notification = IMPOSSIBLE_REVOKE_COMMAND.notification(InputPosition.empty);
verifyNotification(
notification,
"`%s` has no effect.",
SeverityLevel.WARNING,
"Neo.ClientNotification.Security.ImpossibleRevokeCommand",
"%s Make sure nothing is misspelled. This notification will become an error in a future major version. "
+ "See Status Codes documentation for more information.",
NotificationCategory.SECURITY);
}
private void verifyNotification(
Notification notification,
String title,
......@@ -650,8 +679,8 @@ class NotificationCodeWithDescriptionTest {
byte[] notificationHash = DigestUtils.sha256(notificationBuilder.toString());
byte[] expectedHash = new byte[] {
94, 17, 11, 62, -126, 99, -36, -39, 2, -23, 39, 52, 93, 120, -51, 115, 99, 99, 46, 72, -120, -70, -50, -124,
82, -119, 103, -69, -55, 81, 8, -116
22, 5, -49, 108, -14, -76, 71, -29, 60, 16, -107, 52, -13, 73, -112, -80, -31, 121, -91, 17, 84, -125, -106,
-1, -33, 88, -22, -84, 94, 77, 50, 35
};
if (!Arrays.equals(notificationHash, expectedHash)) {
......
......@@ -141,7 +141,13 @@ public enum NotificationCodeWithDescription {
Status.Statement.FeatureDeprecationWarning,
"The Cypher query option `connectComponentsPlanner` is deprecated and will be removed without a replacement. "
+ "The product's default behavior of using a cost-based IDP search algorithm when combining sub-plans will be kept. "
+ "For more information, see Cypher Manual -> Cypher planner.");
+ "For more information, see Cypher Manual -> Cypher planner."),
COMMAND_HAS_NO_EFFECT(
Status.Security.CommandHasNoEffect, "%s See Status Codes documentation for more information."),
IMPOSSIBLE_REVOKE_COMMAND(
Status.Security.ImpossibleRevokeCommand,
"%s Make sure nothing is misspelled. This notification will become an error in a future major version. "
+ "See Status Codes documentation for more information.");
private final Status status;
private final String description;
......@@ -172,4 +178,14 @@ public enum NotificationCodeWithDescription {
.setTitleDetails(details)
.build();
}
public NotificationImplementation notificationWithTitleAndDescriptionDetails(
InputPosition position, String titleDetail, String... descriptionDetails) {
// Allows a single detail in the title and multiple in the description
return new NotificationImplementation.NotificationBuilder(this)
.setPosition(position)
.setTitleDetails(titleDetail)
.setNotificationDetails(descriptionDetails)
.build();
}
}
......@@ -135,7 +135,9 @@ public final class NotificationImplementation implements Notification {
return false;
}
NotificationImplementation that = (NotificationImplementation) o;
return Objects.equals(position, that.position) && Objects.equals(description, that.description);
return Objects.equals(position, that.position)
&& Objects.equals(description, that.description)
&& Objects.equals(title, that.title);
}
@Override
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册