diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 5cf42dd..dfb0272 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -36,6 +36,9 @@
  */
 class EntityResource extends ResourceBase implements DependentPluginInterface {
 
+  use EntityResourceValidationTrait;
+  use EntityResourceAccessTrait;
+
   /**
    * The entity type targeted by this resource.
    *
@@ -156,14 +159,7 @@ 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->checkEditFieldAccess($entity);
 
     // Validate the received data before saving.
     $this->validate($entity);
@@ -175,8 +171,7 @@ 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);
@@ -276,39 +271,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) {
-    // @todo Remove when https://www.drupal.org/node/2164373 is committed.
-    if (!$entity instanceof FieldableEntityInterface) {
-      return;
-    }
-    $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}
    */
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResourceAccessTrait.php b/core/modules/rest/src/Plugin/rest/resource/EntityResourceAccessTrait.php
new file mode 100644
index 0000000..7bf8e82
--- /dev/null
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResourceAccessTrait.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\rest\Plugin\rest\resource;
+
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * @internal
+ * @todo Consider making public in https://www.drupal.org/node/2300677
+ */
+trait EntityResourceAccessTrait {
+
+  /**
+   * Performs edit access checks for fields.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose fields edit access should be checked for.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   *   Throws access denied when the user does not have permissions to edit a
+   *   field.
+   */
+  protected function checkEditFieldAccess(EntityInterface $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'.");
+      }
+    }
+  }
+
+}
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php b/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
new file mode 100644
index 0000000..a2ff40a
--- /dev/null
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\rest\Plugin\rest\resource;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+
+/**
+ * @internal
+ * @todo Consider making public in https://www.drupal.org/node/2300677
+ */
+trait EntityResourceValidationTrait {
+
+  /**
+   * Verifies that the whole entity does not violate any validation constraints.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to validate.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
+   *   If validation errors are found.
+   */
+  protected function validate(EntityInterface $entity) {
+    // @todo Remove when https://www.drupal.org/node/2164373 is committed.
+    if (!$entity instanceof FieldableEntityInterface) {
+      return;
+    }
+    $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";
+      }
+      throw new UnprocessableEntityHttpException($message);
+    }
+  }
+
+}
diff --git a/core/modules/rest/tests/src/Unit/EntityResourceValidationTraitTest.php b/core/modules/rest/tests/src/Unit/EntityResourceValidationTraitTest.php
new file mode 100644
index 0000000..20a6175
--- /dev/null
+++ b/core/modules/rest/tests/src/Unit/EntityResourceValidationTraitTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\Tests\rest\Unit;
+
+use Drupal\Core\Entity\EntityConstraintViolationList;
+use Drupal\node\Entity\Node;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\Entity\User;
+use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+use Symfony\Component\Validator\ConstraintViolationInterface;
+
+/**
+ * @group rest
+ * @coversDefaultClass \Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait
+ */
+class EntityResourceValidationTraitTest extends UnitTestCase {
+
+  /**
+   * @covers ::validate
+   */
+  public function testValidate() {
+    $trait = $this->getMockForTrait('Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait');
+
+    $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());
+  }
+
+  /**
+   * @covers ::validate
+   */
+  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\Plugin\rest\resource\EntityResourceValidationTrait');
+
+    $method = new \ReflectionMethod($trait, 'validate');
+    $method->setAccessible(TRUE);
+
+    $this->setExpectedException(UnprocessableEntityHttpException::class);
+
+    $method->invoke($trait, $entity->reveal());
+  }
+
+}
diff --git a/core/modules/user/src/Plugin/rest/resource/UserRegistrationResource.php b/core/modules/user/src/Plugin/rest/resource/UserRegistrationResource.php
new file mode 100644
index 0000000..6a243c3
--- /dev/null
+++ b/core/modules/user/src/Plugin/rest/resource/UserRegistrationResource.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\user\Plugin\rest\resource;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\rest\ModifiedResourceResponse;
+use Drupal\rest\Plugin\ResourceBase;
+use Drupal\rest\Plugin\rest\resource\EntityResourceAccessTrait;
+use Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait;
+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 Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+
+/**
+ * Represents user registration as a 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 EntityResourceValidationTrait;
+  use EntityResourceAccessTrait;
+
+  /**
+   * 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
+   *   The current user.
+   */
+  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) {
+    $this->ensureAccountCanRegister($account);
+
+    // Only activate new users if visitors are allowed to register and no email
+    // verification required.
+    if ($this->userSettings->get('register') == USER_REGISTER_VISITORS && !$this->userSettings->get('verify_mail')) {
+      $account->activate();
+    }
+    else {
+      $account->block();
+    }
+
+    $this->checkEditFieldAccess($account);
+
+    // Make sure that the user entity is valid (email and name are valid).
+    $this->validate($account);
+
+    // Create the account.
+    $account->save();
+
+    $this->sendEmailNotifications($account);
+
+    return new ModifiedResourceResponse($account, 200);
+  }
+
+  /**
+   * Ensure the account can be registered in this request.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account to register.
+   */
+  protected function ensureAccountCanRegister(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.');
+    }
+
+    // Only allow anonymous users to register, authenticated users with the
+    // necessary permissions can POST a new user to the "user" REST resource.
+    // @see \Drupal\rest\Plugin\rest\resource\EntityResource
+    if (!$this->currentUser->isAnonymous()) {
+      throw new AccessDeniedHttpException('Only anonymous users can register a user.');
+    }
+
+    // Verify that the current user can register a user account.
+    if ($this->userSettings->get('register') == USER_REGISTER_ADMINISTRATORS_ONLY) {
+      throw new AccessDeniedHttpException('You cannot register a new user account.');
+    }
+
+    if (!$this->userSettings->get('verify_mail')) {
+      if (empty($account->getPassword())) {
+        // If no e-mail verification then the user must provide a password.
+        throw new UnprocessableEntityHttpException('No password provided.');
+      }
+    }
+    else {
+      if (!empty($account->getPassword())) {
+        // If e-mail verification required then a password cannot provided.
+        // The password will be set when the user logs in.
+        throw new UnprocessableEntityHttpException('A Password cannot be specified. It will be generated on login.');
+      }
+    }
+  }
+
+  /**
+   * Sends email notifications if necessary for user that was registered.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account.
+   */
+  protected function sendEmailNotifications(UserInterface $account) {
+    $approval_settings = $this->userSettings->get('register');
+    // No e-mail verification is required. Activating the user.
+    if ($approval_settings == USER_REGISTER_VISITORS) {
+      if ($this->userSettings->get('verify_mail')) {
+        // No administrator approval required.
+        _user_mail_notify('register_no_approval_required', $account);
+      }
+    }
+    // Administrator approval required.
+    elseif ($approval_settings == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) {
+      _user_mail_notify('register_pending_approval', $account);
+    }
+  }
+
+}
diff --git a/core/modules/user/src/Tests/RestRegisterUserTest.php b/core/modules/user/src/Tests/RestRegisterUserTest.php
new file mode 100644
index 0000000..d8420ca
--- /dev/null
+++ b/core/modules/user/src/Tests/RestRegisterUserTest.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace Drupal\user\Tests;
+
+use Drupal\rest\Tests\RESTTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\RoleInterface;
+
+/**
+ * Tests user registration via REST resource.
+ *
+ * @group user
+ */
+class RestRegisterUserTest extends RESTTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['hal'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->enableService('user_registration', 'POST', 'hal_json');
+
+    Role::load(RoleInterface::ANONYMOUS_ID)
+      ->grantPermission('restful post user_registration')
+      ->save();
+
+    Role::load(RoleInterface::AUTHENTICATED_ID)
+      ->grantPermission('restful post user_registration')
+      ->save();
+  }
+
+  /**
+   * Tests that only anonymous users can register users.
+   */
+  public function testRegisterUser() {
+    // Verify that an authenticated user cannot register a new user, despite
+    // being granted permission to do so because only anonymous users can
+    // register themselves, authenticated users with the necessary permissions
+    // can POST a new user to the "user" REST resource.
+    $user = $this->createUser();
+    $this->drupalLogin($user);
+    $this->registerRequest('palmer.eldritch');
+    $this->assertResponse('403', 'Only anonymous users can register users.');
+    $this->drupalLogout();
+
+    $user_settings = $this->config('user.settings');
+
+    // Test out different setting User Registration and Email Verification.
+    // Allow visitors to register with no email verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS);
+    $user_settings->set('verify_mail', 0);
+    $user_settings->save();
+    $user = $this->registerUser('Palmer.Eldritch');
+    $this->assertFalse($user->isBlocked());
+    $this->assertFalse(empty($user->getPassword()));
+    $email_count = count($this->drupalGetMails());
+    $this->assertEqual(0, $email_count);
+
+    // Attempt to register without sending a password.
+    $this->registerRequest('Rick.Deckard', FALSE);
+    $this->assertResponse('422', 'No password provided');
+
+    // Allow visitors to register with email verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS);
+    $user_settings->set('verify_mail', 1);
+    $user_settings->save();
+    $user = $this->registerUser('Jason.Taverner', FALSE);
+    $this->assertTrue(empty($user->getPassword()));
+    $this->assertTrue($user->isBlocked());
+    $this->assertMailString('body', 'You may now log in by clicking this link', 1);
+
+    // Attempt to register with a password when e-mail verification is on.
+    $this->registerRequest('Estraven', TRUE);
+    $this->assertResponse('422', 'A Password cannot be specified. It will be generated on login.');
+
+    // Allow visitors to register with Admin approval and e-mail verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
+    $user_settings->set('verify_mail', 1);
+    $user_settings->save();
+    $user = $this->registerUser('Bob.Arctor', FALSE);
+    $this->assertTrue(empty($user->getPassword()));
+    $this->assertTrue($user->isBlocked());
+    $this->assertMailString('body', 'Your application for an account is', 2);
+    $this->assertMailString('body', 'Bob.Arctor has applied for an account', 2);
+
+    // Attempt to register with a password when e-mail verification is on.
+    $this->registerRequest('Ursula', TRUE);
+    $this->assertResponse('422', 'A Password cannot be specified. It will be generated on login.');
+
+    // Allow visitors to register with Admin approval and no email verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
+    $user_settings->set('verify_mail', 0);
+    $user_settings->save();
+    $user = $this->registerUser('Argaven');
+    $this->assertFalse(empty($user->getPassword()));
+    $this->assertTrue($user->isBlocked());
+    $this->assertMailString('body', 'Your application for an account is', 2);
+    $this->assertMailString('body', 'Argaven has applied for an account', 2);
+
+    // Attempt to register without sending a password.
+    $this->registerRequest('Tibe', FALSE);
+    $this->assertResponse('422', 'No password provided');
+  }
+
+  /**
+   * Creates serialize user values.
+   *
+   * @param string $name
+   *   The name of the user. Use only valid values for emails.
+   *
+   * @param bool $include_password
+   *   Whether to include a password in the user values.
+   *
+   * @return string Serialized user values.
+   *   Serialized user values.
+   */
+  protected function createSerializedUser($name, $include_password = TRUE) {
+    global $base_url;
+    // New user info to be serialized.
+    $data = [
+      "_links" =>
+        [
+          "type" => ["href" => $base_url . "/rest/type/user/user"],
+        ],
+      "langcode" => [
+        [
+          "value" => "en",
+        ],
+      ],
+      "name" => [
+        [
+          "value" => $name,
+        ],
+      ],
+      "mail" => [
+        [
+          "value" => "$name@example.com",
+        ],
+      ],
+    ];
+    if ($include_password) {
+      $data['pass']['value'] = 'SuperSecretPassword';
+    }
+
+    // Create a HAL+JSON version for the user entity we want to create.
+    $serialized = $this->container->get('serializer')
+      ->serialize($data, 'hal_json');
+    return $serialized;
+  }
+
+  /**
+   * Registers a user via REST resource.
+   *
+   * @param $name
+   *   User name.
+   *
+   * @param bool $include_password
+   *
+   * @return bool|\Drupal\user\Entity\User
+   *
+   */
+  protected function registerUser($name, $include_password = TRUE) {
+    // Verify that an anonymous user can register.
+    $this->registerRequest($name, $include_password);
+    $this->assertResponse('200', 'HTTP response code is correct.');
+    $user = user_load_by_name($name);
+    $this->assertFalse(empty($user), 'User was create as expected');
+    return $user;
+  }
+
+  /**
+   * Make a REST user registration request.
+   *
+   * @param $name
+   * @param $include_password
+   */
+  protected function registerRequest($name, $include_password = TRUE) {
+    $serialized = $this->createSerializedUser($name, $include_password);
+    $this->httpRequest('/user/register', 'POST', $serialized, 'application/hal+json');
+  }
+
+}
diff --git a/core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php b/core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php
new file mode 100644
index 0000000..142685c
--- /dev/null
+++ b/core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace Drupal\Tests\user\Unit;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\Entity\User;
+use Drupal\user\Plugin\rest\resource\UserRegistrationResource;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+
+/**
+ * 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\user\Plugin\rest\resource\UserRegistrationResource
+ * @group user
+ */
+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\user\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.
+   */
+  public function testEmptyPost() {
+    $this->setExpectedException(BadRequestHttpException::class);
+    $this->testClass->post(NULL);
+  }
+
+  /**
+   * Tests that only new user accounts can be registered.
+   */
+  public function testExistedEntityPost() {
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(FALSE);
+    $this->setExpectedException(BadRequestHttpException::class);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+  /**
+   * Tests that admin permissions are required to register a 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->setExpectedException(AccessDeniedHttpException::class);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+  /**
+   * Tests that 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->setExpectedException(AccessDeniedHttpException::class);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+}
