diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 63857e4..5ae3dcd 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -10,6 +10,7 @@
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
+use \Drupal\rest\ResourceValidationTrait;
 
 /**
  * Represents entities as resources.
@@ -29,6 +30,8 @@
  */
 class EntityResource extends ResourceBase {
 
+  use ResourceValidationTrait;
+
   /**
    * Responds to entity GET requests.
    *
@@ -93,17 +96,11 @@ public function post(EntityInterface $entity = NULL) {
       throw new BadRequestHttpException('Only new entities can be created');
     }
 
-    // Only check 'edit' permissions for fields that were actually
-    // submitted by the user. Field access makes no difference between 'create'
-    // and 'update', so the 'edit' operation is used here.
-    foreach ($entity->_restSubmittedFields as $key => $field_name) {
-      if (!$entity->get($field_name)->access('edit')) {
-        throw new AccessDeniedHttpException("Access denied on creating field '$field_name'");
-      }
-    }
+    $this->fieldsAccess($entity);
 
     // Validate the received data before saving.
     $this->validate($entity);
+
     try {
       $entity->save();
       $this->logger->notice('Created entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id()));
@@ -112,8 +109,8 @@ public function post(EntityInterface $entity = NULL) {
       // body. These responses are not cacheable, so we add no cacheability
       // metadata here.
       $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE);
-      $response = new ModifiedResourceResponse($entity, 201, ['Location' => $url->getGeneratedUrl()]);
-      return $response;
+
+      return new ModifiedResourceResponse($entity, 201, ['Location' => $url->getGeneratedUrl()]);
     }
     catch (EntityStorageException $e) {
       throw new HttpException(500, 'Internal Server Error', $e);
@@ -174,6 +171,7 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
 
     // Validate the received data before saving.
     $this->validate($original_entity);
+
     try {
       $original_entity->save();
       $this->logger->notice('Updated entity %type with ID %id.', array('%type' => $original_entity->getEntityTypeId(), '%id' => $original_entity->id()));
@@ -214,35 +212,6 @@ public function delete(EntityInterface $entity) {
   }
 
   /**
-   * Verifies that the whole entity does not violate any validation constraints.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   *
-   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
-   *   If validation errors are found.
-   */
-  protected function validate(EntityInterface $entity) {
-    $violations = $entity->validate();
-
-    // Remove violations of inaccessible fields as they cannot stem from our
-    // changes.
-    $violations->filterByFieldAccess();
-
-    if (count($violations) > 0) {
-      $message = "Unprocessable Entity: validation failed.\n";
-      foreach ($violations as $violation) {
-        $message .= $violation->getPropertyPath() . ': ' . $violation->getMessage() . "\n";
-      }
-      // Instead of returning a generic 400 response we use the more specific
-      // 422 Unprocessable Entity code from RFC 4918. That way clients can
-      // distinguish between general syntax errors in bad serializations (code
-      // 400) and semantic errors in well-formed requests (code 422).
-      throw new HttpException(422, $message);
-    }
-  }
-
-  /**
    * {@inheritdoc}
    */
   protected function getBaseRoute($canonical_path, $method) {
diff --git a/core/modules/rest/src/Plugin/rest/resource/UserRegistrationResource.php b/core/modules/rest/src/Plugin/rest/resource/UserRegistrationResource.php
new file mode 100644
index 0000000..1c86b97
--- /dev/null
+++ b/core/modules/rest/src/Plugin/rest/resource/UserRegistrationResource.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Drupal\rest\Plugin\rest\resource;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\rest\ModifiedResourceResponse;
+use Drupal\user\UserInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+use Drupal\rest\Plugin\ResourceBase;
+use \Drupal\rest\ResourceValidationTrait;
+
+/**
+ * Represents user registration as resource.
+ *
+ * @RestResource(
+ *   id = "user_registration",
+ *   label = @Translation("User registration"),
+ *   serialization_class = "Drupal\user\Entity\User",
+ *   uri_paths = {
+ *     "https://www.drupal.org/link-relations/create" = "/user/register",
+ *   },
+ * )
+ */
+class UserRegistrationResource extends ResourceBase {
+
+  use ResourceValidationTrait;
+
+  /**
+   * User settings config instance.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig
+   */
+  protected $userSettings;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new UserRegistrationResource instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param array $serializer_formats
+   *   The available serialization formats.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   A logger instance.
+   * @param \Drupal\Core\Config\ImmutableConfig $user_settings
+   *   A user settings config instance.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   A user settings config instance.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, ImmutableConfig $user_settings, AccountInterface $current_user) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
+    $this->userSettings = $user_settings;
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->getParameter('serializer.formats'),
+      $container->get('logger.factory')->get('rest'),
+      $container->get('config.factory')->get('user.settings'),
+      $container->get('current_user')
+    );
+  }
+
+  /**
+   * Responds to user registration POST request.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account entity.
+   *
+   * @return \Drupal\rest\ModifiedResourceResponse
+   *   The HTTP response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   */
+  public function post(UserInterface $account = NULL) {
+    if ($account == NULL) {
+      throw new BadRequestHttpException('No user account data for registration received.');
+    }
+
+    // POSTed user accounts must not have an ID set, because we always want to
+    // create new entities here.
+    if (!$account->isNew()) {
+      throw new BadRequestHttpException('An ID has been set and only new user accounts can be registered.');
+    }
+
+    // The current resource only allows anonymous users to register users.
+    if (!$this->currentUser->isAnonymous()) {
+      throw new AccessDeniedHttpException('Only anonymous users can register users.');
+    }
+    $approvalSettings = $this->userSettings->get('register');
+
+    // Verify that the current user can register a user account.
+    if ($approvalSettings == USER_REGISTER_ADMINISTRATORS_ONLY) {
+      throw new AccessDeniedHttpException('You cannot register a new user account.');
+    }
+    // If current user can register accounts then let's block the new registered
+    // user if admin approval is needed.
+    elseif ($approvalSettings == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) {
+      $account->block();
+    }
+
+    $this->fieldsAccess($account);
+
+    // Make sure that the user entity is valid (email and name are valid).
+    $this->validate($account);
+
+    // Create the account.
+    $account->save();
+
+    $register = $this->userSettings->get('register');
+    // No e-mail verification is required. Activating the user.
+    if ($register == 'visitors') {
+      if (!$this->userSettings->get('verify_mail')) {
+        // Notification will be sent if activated.
+        $account->activate();
+        // Save changes to apply active status to the account.
+        $account->save();
+      }
+      // No administrator approval required.
+      else {
+        _user_mail_notify('register_no_approval_required', $account);
+      }
+    }
+    // Administrator approval required.
+    elseif ($register == 'visitors_admin_approval') {
+      _user_mail_notify('register_pending_approval', $account);
+    }
+
+    return new ModifiedResourceResponse($account, 200);
+  }
+
+}
diff --git a/core/modules/rest/src/ResourceValidationTrait.php b/core/modules/rest/src/ResourceValidationTrait.php
new file mode 100644
index 0000000..22ded20
--- /dev/null
+++ b/core/modules/rest/src/ResourceValidationTrait.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\rest;
+
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+
+trait ResourceValidationTrait {
+
+  /**
+   * Verifies that the whole entity does not violate any validation constraints.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
+   *   If validation errors are found.
+   */
+  protected function validate(EntityInterface $entity) {
+
+    $violations = $entity->validate();
+
+    // Remove violations of inaccessible fields as they cannot stem from our
+    // changes.
+    $violations->filterByFieldAccess();
+
+    if ($violations->count() > 0) {
+      $message = "Unprocessable Entity: validation failed.\n";
+      foreach ($violations as $violation) {
+        $message .= $violation->getPropertyPath() . ': ' . $violation->getMessage() . "\n";
+      }
+      // Instead of returning a generic 400 response we use the more specific
+      // 422 Unprocessable Entity code from RFC 4918. That way clients can
+      // distinguish between general syntax errors in bad serializations (code
+      // 400) and semantic errors in well-formed requests (code 422).
+      throw new HttpException(422, $message);
+    }
+  }
+
+  /**
+   * Checks access for entity fields.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity object.
+   */
+  protected function fieldsAccess($entity) {
+    // Only check 'edit' permissions for fields that were actually submitted by
+    // the user. Field access makes no difference between 'create'and 'update',
+    // so the 'edit' operation is used here.
+    foreach ($entity->_restSubmittedFields as $key => $field_name) {
+      if (!$entity->get($field_name)->access('edit')) {
+        throw new AccessDeniedHttpException("Access denied on creating field '$field_name'.");
+      }
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/core/modules/rest/src/Tests/RegisterUserTest.php b/core/modules/rest/src/Tests/RegisterUserTest.php
new file mode 100644
index 0000000..3e9bd2f
--- /dev/null
+++ b/core/modules/rest/src/Tests/RegisterUserTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\rest\Tests;
+
+use Drupal\user\Entity\Role;
+use Drupal\user\RoleInterface;
+
+/**
+ * Tests User Registration.
+ *
+ * @group rest
+ */
+class RegisterUserTest extends RESTTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['hal'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->enableService('user_registration', 'POST', 'hal_json');
+
+    // Add registration permission to anonymous user.
+    Role::load(RoleInterface::ANONYMOUS_ID)
+      ->grantPermission('restful post user_registration')
+      ->save();
+
+    // Add registration permission to authenticated user.
+    Role::load(RoleInterface::AUTHENTICATED_ID)
+      ->grantPermission('restful post user_registration')
+      ->save();
+  }
+
+  /**
+   * Tests user registration from REST.
+   */
+  public function testRegisterUser() {
+    global $base_url;
+
+    // New user info to be serialized.
+    $data = [
+      "_links" =>
+        [
+          "type" => ["href" => $base_url . "/rest/type/user/user"]
+        ],
+      "langcode" =>  [
+        [
+          "value" => "en"
+        ]
+      ],
+      "name" => [
+        [
+          "value" => "Druplicon"
+        ]
+      ],
+      "mail" => [
+        [
+          "value" => "druplicon@example.com"
+        ]
+      ],
+      "pass" => [
+        [
+          "value" => "SuperSecretPassword"
+        ]
+      ]
+    ];
+
+    // Create a JSON version for the user entity we want to create.
+    $serialized = $this->container->get('serializer')->serialize($data, 'hal_json');
+
+    // Verify that an authenticated user cannot register a new user using the
+    // user_registration REST resource.
+    $user = $this->createUser();
+    $this->drupalLogin($user);
+
+    // Post to the REST service to register the user.
+    $this->httpRequest('/user/register', 'POST', $serialized, 'application/hal+json');
+    $this->assertResponse('403', 'Only anonymous users can register users.');
+
+    $this->drupalLogout();
+
+    // Post to the REST service to register the user.
+    $this->httpRequest('/user/register', 'POST', $serialized, 'application/hal+json');
+    $this->assertResponse('200', 'HTTP response code is correct.');
+    
+    $this->assertTrue((bool) $this->container->get('entity.query')
+      ->get('user')
+      ->condition('name', 'Druplicon')
+      ->range(0, 1)
+      ->count()
+      ->execute(), 'The user was created as expected');
+  }
+
+}
diff --git a/core/modules/rest/tests/src/Unit/ResourceValidationTraitTest.php b/core/modules/rest/tests/src/Unit/ResourceValidationTraitTest.php
new file mode 100644
index 0000000..03bc46c
--- /dev/null
+++ b/core/modules/rest/tests/src/Unit/ResourceValidationTraitTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Drupal\Tests\rest\Unit;
+
+use Drupal\Core\Entity\Entity;
+use Drupal\Core\Entity\EntityConstraintViolationList;
+use Drupal\node\Entity\Node;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\Entity\User;
+use Symfony\Component\Validator\ConstraintViolationInterface;
+
+/**
+ * @group rest
+ * @coversDefaultClass \Drupal\rest\ResourceValidationTrait
+ */
+class ResourceValidationTraitTest extends UnitTestCase {
+
+  /**
+   * Tests that the user entity does not violate any validation constraints.
+   */
+  public function testValidate() {
+
+    $trait = $this->getMockForTrait('Drupal\rest\ResourceValidationTrait');
+
+    $method = new \ReflectionMethod($trait, 'validate');
+    $method->setAccessible(TRUE);
+
+    $entity = $this->prophesize(Node::class);
+
+    $violations = $this->prophesize(EntityConstraintViolationList::class);
+    $violations->filterByFieldAccess()->willReturn([]);
+    $violations->count()->willReturn(0);
+
+    $entity->validate()->willReturn($violations->reveal());
+
+    $method->invoke($trait, $entity->reveal());
+  }
+
+  /**
+   * Tests that error validation is thrown as expected.
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\HttpException
+   * @expectedException UserRegistrationResourceTest::ERROR_MESSAGE
+   */
+  public function testFailedValidate() {
+    $violation1 = $this->prophesize(ConstraintViolationInterface::class);
+    $violation1->getPropertyPath()->willReturn('property_path');
+    $violation1->getMessage()->willReturn('message');
+
+    $violation2 = $this->prophesize(ConstraintViolationInterface::class);
+    $violation2->getPropertyPath()->willReturn('property_path');
+    $violation2->getMessage()->willReturn('message');
+
+    $entity = $this->prophesize(User::class);
+
+    $violations = $this->getMockBuilder(EntityConstraintViolationList::class)
+      ->setConstructorArgs([$entity->reveal(), [$violation1->reveal(), $violation2->reveal()]])
+      ->setMethods(['filterByFieldAccess'])
+      ->getMock();
+
+    $violations->expects($this->once())
+      ->method('filterByFieldAccess')
+      ->will($this->returnValue([]));
+
+    $entity->validate()->willReturn($violations);
+
+    $trait = $this->getMockForTrait('Drupal\rest\ResourceValidationTrait');
+
+    $method = new \ReflectionMethod($trait, 'validate');
+    $method->setAccessible(TRUE);
+
+    $method->invoke($trait, $entity->reveal());
+  }
+}
\ No newline at end of file
diff --git a/core/modules/rest/tests/src/Unit/UserRegistrationResourceTest.php b/core/modules/rest/tests/src/Unit/UserRegistrationResourceTest.php
new file mode 100644
index 0000000..69c5b0d
--- /dev/null
+++ b/core/modules/rest/tests/src/Unit/UserRegistrationResourceTest.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Drupal\Tests\rest\Unit;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\rest\Plugin\rest\resource\UserRegistrationResource;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\Entity\User;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Only administrators can create user accounts.
+ */
+if (!defined('USER_REGISTER_ADMINISTRATORS_ONLY')) {
+  define('USER_REGISTER_ADMINISTRATORS_ONLY', 'admin_only');
+}
+
+/**
+ * Visitors can create their own accounts.
+ */
+if (!defined('USER_REGISTER_VISITORS')) {
+  define('USER_REGISTER_VISITORS', 'visitors');
+}
+
+/**
+ * Visitors can create accounts, but they don't become active without
+ * administrative approval.
+ */
+if (!defined('USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL')) {
+  define('USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL', 'visitors_admin_approval');
+}
+
+/**
+ * Tests User Registration REST resource.
+ *
+ * @coversDefaultClass \Drupal\Tests\rest\Unit\UserRegistrationResourceTest
+ * @group rest
+ */
+class UserRegistrationResourceTest extends UnitTestCase {
+
+  const ERROR_MESSAGE = "Unprocessable Entity: validation failed.\nproperty_path: message\nproperty_path_2: message_2\n";
+
+  /**
+   * Class to be tested.
+   *
+   * @var \Drupal\rest\Plugin\rest\resource\UserRegistrationResource
+   */
+  protected $testClass;
+
+  /**
+   * A reflection of self::$testClass.
+   *
+   * @var \ReflectionClass
+   */
+  protected $reflection;
+
+  /**
+   * A user settings config instance.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $userSettings;
+
+  /**
+   * Logger service.
+   *
+   * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $logger;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $currentUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->logger = $this->prophesize(LoggerInterface::class)->reveal();
+
+    $this->userSettings = $this->prophesize(ImmutableConfig::class);
+
+    $this->currentUser = $this->prophesize(AccountInterface::class);
+
+    $this->testClass = new UserRegistrationResource([], 'plugin_id', '', [], $this->logger, $this->userSettings->reveal(), $this->currentUser->reveal());
+    $this->reflection = new \ReflectionClass($this->testClass);
+  }
+
+  /**
+   * Tests that an exception is thrown when no data provided for the account.
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   * @expectedExceptionMessage No user account data for registration received.
+   */
+  public function testEmptyPost() {
+    $this->testClass->post(NULL);
+  }
+
+  /**
+   * Tests that only new user accounts can be registered.
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   * @expectedExceptionMessage An ID has been set and only new user accounts can be registered.
+   */
+  public function testExistedEntityPost() {
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(FALSE);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+  /**
+   * Tests that admin permissions are required to register a user account.
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   * @expectedExceptionMessage You cannot register a new user account.
+   */
+  public function testRegistrationAdminOnlyPost() {
+
+    $this->userSettings->get('register')->willReturn(USER_REGISTER_ADMINISTRATORS_ONLY);
+
+    $this->currentUser->isAnonymous()->willReturn(TRUE);
+
+    $this->testClass = new UserRegistrationResource([], 'plugin_id', '', [], $this->logger, $this->userSettings->reveal(), $this->currentUser->reveal());
+
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(TRUE);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+  /**
+   * Tests that only anonymous users can register users.
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   * @expectedExceptionMessage Only anonymous users can register users.
+   */
+  public function testRegistrationAnonymousOnlyPost() {
+    $this->currentUser->isAnonymous()->willReturn(FALSE);
+
+    $this->testClass = new UserRegistrationResource([], 'plugin_id', '', [], $this->logger, $this->userSettings->reveal(), $this->currentUser->reveal());
+
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(TRUE);
+
+    $this->testClass->post($entity->reveal());
+  }
+}
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index cb454a1..04d1927 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -2680,7 +2680,7 @@ protected function prepareRequestForGenerator($clean_urls = TRUE, $override_serv
    *   Options to be passed to Url::fromUri().
    *
    * @return string
-   *   An absolute URL stsring.
+   *   An absolute URL string.
    */
   protected function buildUrl($path, array $options = array()) {
     if ($path instanceof Url) {
