Skip to content

Commit

Permalink
Add create restriction on properties, refs 2232 (#2600)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwjames committed Aug 12, 2017
1 parent 889918b commit 9de468c
Show file tree
Hide file tree
Showing 30 changed files with 816 additions and 144 deletions.
15 changes: 15 additions & 0 deletions DefaultSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,21 @@
'smwgQueryResultCacheRefreshOnPurge' => true,
##

##
# Property create protection
#
# If enabled, users are able to annotate property values using existing properties
# but new properties can only be created by those with the assigned "authority"
# (aka user right).
#
# Changes to a property specification requires the permission as well.
#
# @since 3.0
# @default false
##
'smwgCreateProtectionRight' => false,
##

###
# Page edit protection
#
Expand Down
4 changes: 3 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@
"smw-pa-property-predefined_pval": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value \"$1\"] is a predefined property that can define a list of permissible values to restrict value assignments for a property and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
"smw-pa-property-predefined_pvali": "[https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_value_list \"$1\"] is a predefined property that can specify a reference to a list that holds permissible values to restrict value assignments for a property and is provided by [https://www.semantic-mediawiki.org/wiki/Help:Special_properties Semantic MediaWiki].",
"smw-datavalue-property-restricted-use": "Property \"$1\" has been marked for restricted use.",
"smw-datavalue-property-create-restriction": "Property \"$1\" doesn't exist and the user is missing the \"$2\" permission (see [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode]) to create or annotate values with an unapproved property.",
"smw-datavalue-property-invalid-character": "\"$1\" contains the \"$2\" character as part of a property label and has been classified as invalid.",
"smw-datavalue-property-invalid-chain": "Using \"$1\" as property chain is not permitted during the annotation process.",
"smw-datavalue-restricted-use": "Datavalue \"$1\" has been marked for restricted use.",
Expand Down Expand Up @@ -542,7 +543,8 @@
"smw-property-req-violation-change-propagation-locked": "Property \"$1\" was modified and triggered a [https://www.semantic-mediawiki.org/wiki/Change_propagation change propagation] process which at the same time locked the property page to prevent intermediary interruptions or contradictory specifications up to the point where the primary data has been updated. The process may take moment before the page can be unlocked as it depends on the size and frequency of the [https://www.mediawiki.org/wiki/Manual:Job_queue job queue] scheduler.",
"smw-property-req-violation-change-propagation-pending": "[https://www.semantic-mediawiki.org/wiki/Change_propagation Change propagation] updates are pending ($1 [https://www.mediawiki.org/wiki/Manual:Job_queue {{PLURAL:$1|job|jobs}}] estimated) and it is recommended to wait with modifications to a property until the process has been finalized to prevent intermediary interruptions or contradictory specifications.",
"protect-level-smw-pageedit":"Allow only users with page edit permission (Semantic MediaWiki)",
"smw-edit-protection": "This page is [[Property:Is edit protected|protected]] to prevent accidental data modification and therefore can only be edited by users with the appropriate \"$1\" [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups right].",
"smw-create-protection": "Changes to the \"$1\" property are not permitted for users without the appropriate \"$2\" right or [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups user group] assignment while the [https://www.semantic-mediawiki.org/wiki/Help:Authority_mode authority mode] is enabled.",
"smw-edit-protection": "This page is [[Property:Is edit protected|protected]] to prevent accidental data modification and can only be edited by users with the appropriate edit right (\"$1\") or [https://www.semantic-mediawiki.org/wiki/Help:User_rights_and_user_groups user group].",
"smw-edit-protection-disabled": "The edit protection has been disabled therefore \"$1\" cannot be used to protect entity pages from unauthorized editing.",
"smw-edit-protection-auto-update":"Semantic MediaWiki has updated the protection status according to the \"Is edit protected\" property.",
"smw-edit-protection-enabled":"Edit protected (Semantic MediaWiki)",
Expand Down
1 change: 1 addition & 0 deletions includes/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ public static function newFromGlobals() {
'smwgQueryResultNonEmbeddedCacheLifetime' => $GLOBALS['smwgQueryResultNonEmbeddedCacheLifetime'],
'smwgQueryResultCacheRefreshOnPurge' => $GLOBALS['smwgQueryResultCacheRefreshOnPurge'],
'smwgEditProtectionRight' => $GLOBALS['smwgEditProtectionRight'],
'smwgCreateProtectionRight' => $GLOBALS['smwgCreateProtectionRight'],
'smwgSimilarityLookupExemptionProperty' => $GLOBALS['smwgSimilarityLookupExemptionProperty'],
'smwgPropertyInvalidCharacterList' => $GLOBALS['smwgPropertyInvalidCharacterList'],
'smwgEntityCollation' => $GLOBALS['smwgEntityCollation'],
Expand Down
6 changes: 1 addition & 5 deletions includes/articlepages/SMW_PropertyPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,7 @@ protected function getIntroductoryText() {

$propertySpecificationReqExaminer = new PropertySpecificationReqExaminer(
$this->store,
$applicationFactory->singleton( 'EditProtectionValidator' )
);

$propertySpecificationReqExaminer->setEditProtectionRight(
$applicationFactory->getSettings()->get( 'smwgEditProtectionRight' )
$applicationFactory->singleton( 'ProtectionValidator' )
);

$propertySpecificationReqMsgBuilder = new PropertySpecificationReqMsgBuilder(
Expand Down
27 changes: 24 additions & 3 deletions includes/datavalues/SMW_DV_Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,33 @@ public function isVisible() {
}

/**
* @since 2.2
* @since 3.0
*
* @return boolean
*/
public function canUse() {
return $this->isValid() && $this->m_dataitem->isUnrestricted();
public function isRestricted() {

if ( !$this->isValid() ) {
return true;
}

$propertyRestrictionExaminer = $this->dataValueServiceFactory->getPropertyRestrictionExaminer();

$propertyRestrictionExaminer->isQueryContext(
$this->getOption( self::OPT_QUERY_CONTEXT )
);

$propertyRestrictionExaminer->checkRestriction(
$this->m_dataitem
);

if ( !$propertyRestrictionExaminer->hasRestriction() ) {
return false;
}

$this->restrictionError = $propertyRestrictionExaminer->getError();

return true;
}

/**
Expand Down
25 changes: 24 additions & 1 deletion includes/datavalues/SMW_DataValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ abstract class SMWDataValue {
*/
private $mHasErrors = false;

/**
* @var false|array
*/
protected $restrictionError = false;

/**
* @var Options
*/
Expand Down Expand Up @@ -754,14 +759,23 @@ public function isValid() {
* while usability is determined by its accessibility to a context
* (permission, convention etc.)
*
* @since 2.2
* @since 2.2
*
* @return boolean
*/
public function canUse() {
return true;
}

/**
* @since 3.0
*
* @return boolean
*/
public function isRestricted() {
return false;
}

/**
* @since 2.3
*
Expand Down Expand Up @@ -795,6 +809,15 @@ public function getErrors() {
return $this->mErrors;
}

/**
* @since 3.0
*
* @return array|false
*/
public function getRestrictionError() {
return $this->restrictionError;
}

/**
* Check if property is range restricted and, if so, whether the current value is allowed.
* Creates an error if the value is illegal.
Expand Down
9 changes: 9 additions & 0 deletions includes/query/SMW_QueryParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,15 @@ private function getPropertyDescription( $propertyName, &$setNS ) {
return null; ///TODO: read some more chunks and try to finish [[ ]]
}

// Avoid restriction check on pre-defined properties (Has query, Modification date etc.)
$propertyValue->setOption( $propertyValue::OPT_QUERY_CONTEXT, true );

// Check restriction
if ( $propertyValue->isRestricted() ) {
$this->descriptionProcessor->addError( $propertyValue->getRestrictionError() );
return null;
}

$typeid = $propertyValue->getDataItem()->findPropertyTypeID();
$inverse = $propertyValue->isInverse();
$propertyValueList[] = $propertyValue;
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<var name="wgUseFileCache" value="false"/>
<var name="smwgEntityCollation" value="identity"/>
<var name="smwgFieldTypeFeatures" value="false"/>
<var name="smwgCreateProtectionRight" value="false"/>
<var name="smwgSparqlDefaultGraph" value="http://example.org/phpunit-testrun"/>
<var name="smwgSparqlQFeatures" value="false"/>
<var name="smwgValueLookupCacheType" value="hash"/>
Expand Down
8 changes: 5 additions & 3 deletions src/DataValueFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,17 @@ public function newDataValueByText( $propertyName, $valueString, $caption = fals
return $propertyDV;
}

if ( !$propertyDV->canUse() ) {
if ( $propertyDV->isRestricted() ) {
$dataValue = new ErrorValue(
$propertyDV->getPropertyTypeID(),
array( 'smw-datavalue-property-restricted-use', $propertyName ),
$propertyDV->getRestrictionError(),
$valueString,
$caption
);

$dataValue->setProperty( $propertyDV->getDataItem() );
if ( $propertyDV->getDataItem() instanceof DIProperty ) {
$dataValue->setProperty( $propertyDV->getDataItem() );
}

return $dataValue;
}
Expand Down
8 changes: 2 additions & 6 deletions src/MediaWiki/Hooks/HookRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,7 @@ private function addCallbackHandlers( $basePath, $globalVars ) {
);

$permissionPthValidator = new PermissionPthValidator(
$applicationFactory->singleton( 'EditProtectionValidator' )
);

$permissionPthValidator->setEditProtectionRight(
$applicationFactory->getSettings()->get( 'smwgEditProtectionRight' )
$applicationFactory->singleton( 'ProtectionValidator' )
);

/**
Expand Down Expand Up @@ -552,7 +548,7 @@ private function addCallbackHandlers( $basePath, $globalVars ) {
* to perform an action ..."
*/
$this->handlers['TitleQuickPermissions'] = function ( $title, $user, $action, &$errors, $rigor, $short ) use ( $permissionPthValidator ) {
return $permissionPthValidator->checkQuickPermissionOn( $title, $user, $action, $errors );
return $permissionPthValidator->checkQuickPermission( $title, $user, $action, $errors );
};

$this->registerHooksForInternalUse( $applicationFactory, $deferredRequestDispatchManager );
Expand Down
14 changes: 11 additions & 3 deletions src/ParserFunctions/AskParserFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,20 @@ private function addProcessingError( $errors ) {
$this->parserData->getSubject()
);

$property = new DIProperty( '_ASK' );

foreach ( $errors as $error ) {

if ( ( $property = $processingErrorMsgHandler->grepPropertyFromRestrictionErrorMsg( $error ) ) === null ) {
$property = new DIProperty( '_ASK' );
}

$container = $processingErrorMsgHandler->newErrorContainerFromMsg(
$error,
$property
);

$processingErrorMsgHandler->addToSemanticData(
$this->parserData->getSemanticData(),
$processingErrorMsgHandler->newErrorContainerFromMsg( $error, $property )
$container
);
}
}
Expand Down
63 changes: 34 additions & 29 deletions src/PermissionPthValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Title;
use User;
use SMW\Protection\EditProtectionValidator;
use SMW\Protection\ProtectionValidator;
use SMW\DataValues\AllowsPatternValue;

/**
Expand All @@ -16,31 +16,17 @@
class PermissionPthValidator {

/**
* @var EditProtectionValidator
* @var ProtectionValidator
*/
private $editProtectionValidator;

/**
* @var string|false
*/
private $editProtectionRight = false;

/**
* @since 2.5
*
* @param EditProtectionValidator $editProtectionValidator
*/
public function __construct( EditProtectionValidator $editProtectionValidator ) {
$this->editProtectionValidator = $editProtectionValidator;
}
private $protectionValidator;

/**
* @since 2.5
*
* @param string|false $editProtectionRight
* @param ProtectionValidator $protectionValidator
*/
public function setEditProtectionRight( $editProtectionRight ) {
$this->editProtectionRight = $editProtectionRight;
public function __construct( ProtectionValidator $protectionValidator ) {
$this->protectionValidator = $protectionValidator;
}

/**
Expand All @@ -53,8 +39,8 @@ public function setEditProtectionRight( $editProtectionRight ) {
*
* @return boolean
*/
public function checkQuickPermissionOn( Title &$title, User $user, $action, &$errors ) {
return $this->checkUserPermissionOn( $title, $user, $action, $errors );
public function checkQuickPermission( Title &$title, User $user, $action, &$errors ) {
return $this->hasUserPermission( $title, $user, $action, $errors );
}

/**
Expand All @@ -67,14 +53,18 @@ public function checkQuickPermissionOn( Title &$title, User $user, $action, &$er
*
* @return boolean
*/
public function checkUserPermissionOn( Title &$title, User $user, $action, &$errors ) {
public function hasUserPermission( Title &$title, User $user, $action, &$errors ) {

if ( $action !== 'edit' && $action !== 'delete' && $action !== 'move' && $action !== 'upload' ) {
return true;
}

if ( $title->getNamespace() === NS_MEDIAWIKI ) {
return $this->checkMwNamespaceEditPermission( $title, $user, $action, $errors );
return $this->checkMwNamespacePatternEditPermission( $title, $user, $action, $errors );
}

if ( $this->protectionValidator->getCreateProtectionRight() && $title->getNamespace() === SMW_NS_PROPERTY ) {
return $this->checkPropertyNamespaceCreatePermission( $title, $user, $action, $errors );
}

if ( !$title->exists() ) {
Expand All @@ -85,14 +75,14 @@ public function checkUserPermissionOn( Title &$title, User $user, $action, &$err
return $this->checkPropertyNamespaceEditPermission( $title, $user, $action, $errors );
}

if ( $this->editProtectionRight && $this->editProtectionValidator->hasProtectionOnNamespace( $title ) ) {
if ( $this->protectionValidator->hasEditProtectionOnNamespace( $title ) ) {
return $this->checkEditPermissionOn( $title, $user, $action, $errors );
}

return true;
}

private function checkMwNamespaceEditPermission( Title &$title, User $user, $action, &$errors ) {
private function checkMwNamespacePatternEditPermission( Title &$title, User $user, $action, &$errors ) {

// @see https://www.semantic-mediawiki.org/wiki/Help:Special_property_Allows_pattern
if ( $title->getDBKey() !== AllowsPatternValue::REFERENCE_PAGE_ID || $user->isAllowed( 'smw-patternedit' ) ) {
Expand All @@ -104,10 +94,23 @@ private function checkMwNamespaceEditPermission( Title &$title, User $user, $act
return false;
}

private function checkPropertyNamespaceCreatePermission( Title &$title, User $user, $action, &$errors ) {

$createProtectionRight = $this->protectionValidator->getCreateProtectionRight();

if ( $user->isAllowed( $createProtectionRight ) ) {
return $this->checkPropertyNamespaceEditPermission( $title, $user, $action, $errors );;
}

$errors[] = array( 'smw-create-protection', $createProtectionRight );

return false;
}

private function checkPropertyNamespaceEditPermission( Title &$title, User $user, $action, &$errors ) {

// This renders full protection until the ChangePropagationDispatchJob was run
if ( !$this->editProtectionValidator->hasChangePropagationProtection( $title ) ) {
if ( !$this->protectionValidator->hasChangePropagationProtection( $title ) ) {
return $this->checkEditPermissionOn( $title, $user, $action, $errors );
}

Expand All @@ -118,12 +121,14 @@ private function checkPropertyNamespaceEditPermission( Title &$title, User $user

private function checkEditPermissionOn( Title &$title, User $user, $action, &$errors ) {

$editProtectionRight = $this->protectionValidator->getEditProtectionRight();

// @see https://www.semantic-mediawiki.org/wiki/Help:Special_property_Is_edit_protected
if ( !$this->editProtectionValidator->hasProtection( $title ) || $user->isAllowed( $this->editProtectionRight ) ) {
if ( !$this->protectionValidator->hasProtection( $title ) || $user->isAllowed( $editProtectionRight ) ) {
return true;
}

$errors[] = array( 'smw-edit-protection', $this->editProtectionRight );
$errors[] = array( 'smw-edit-protection', $editProtectionRight );

return false;
}
Expand Down
11 changes: 11 additions & 0 deletions src/ProcessingErrorMsgHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ public function __construct( DIWikiPage $subject ) {
$this->subject = $subject;
}

/**
* @since 3.0
*
* @param string $message
*
* @return DIProperty|null
*/
public static function grepPropertyFromRestrictionErrorMsg( $message ) {
return PropertyRestrictionExaminer::grepPropertyFromRestrictionErrorMsg( $message );
}

/**
* Turns an encoded array of messages or text elements into a compacted array
* with msg keys and arguments.
Expand Down
Loading

0 comments on commit 9de468c

Please sign in to comment.