156 lines
5.8 KiB
Markdown
156 lines
5.8 KiB
Markdown
---
|
||
title: Null Users and System Users in Drupal
|
||
date: 2018-08-16
|
||
excerpt: Announcing the Null User and System User modules.
|
||
tags:
|
||
- drupal
|
||
- drupal-7
|
||
- drupal-8
|
||
- drupal-modules
|
||
- drupal-planet
|
||
- php
|
||
---
|
||
|
||
Have you ever needed to have a 'special user' to perform tasks on your Drupal
|
||
site, such as performing actions based on an API request, or for sending an
|
||
internal site message?
|
||
|
||
If you just create a new user, how do you identify that user going forward? Do
|
||
you hard-code the 'magic' user ID in your custom code? What if the user has a
|
||
different ID on different environments of your site? You could declare it in
|
||
each environment’s settings file and retrieve it from there, but what then if
|
||
you need to do the same on another site? That would mean some duplication of
|
||
code - and something that could have been abstracted and re-used.
|
||
|
||
I had to do this recently, and rather than just duplicate the code I decided to
|
||
make it into it’s own module - which then became two modules.
|
||
|
||
## System users
|
||
|
||
The [System User module][1] provides a re-usable, generic way to denote users as
|
||
'system users', which is not specific to a certain site or environment as this
|
||
is value is stored against each individual user in the database.
|
||
|
||
'System user' is a term used in Linux, which I thought also applies well to this
|
||
scenario.
|
||
|
||
From <https://www.ssh.com/iam/user/system-account>:
|
||
|
||
> A system account is a user account that is created by an operating system
|
||
> during installation and that is used for operating system defined purposes.
|
||
> System accounts often have predefiend user ids. Examples of system accounts
|
||
> include the root account in Linux.
|
||
|
||
A system user isn’t an account that we’d expect a person to log in with and
|
||
perform routine tasks like updating content, but rather for the system (site) to
|
||
use to perform tasks like the earlier examples.
|
||
|
||
### Declaring a user as a system user
|
||
|
||
System User module adds a base field to Drupal’s User entity, which determines
|
||
whether or not each user is a system user - i.e. if this field is `TRUE`, that
|
||
user is a system user. This means that users can easily be queried to identify
|
||
which are system users, without having to rely on magic, environment and site
|
||
specific user IDs. This also means that we can have multiple system users, if
|
||
needed.
|
||
|
||
![](/images/blog/null-users-system-users/drupal-8-users-field-data-table.png){.border
|
||
.p-1}
|
||
|
||
In the Drupal 8 version of the module, a `SystemUser` is a custom entity, that
|
||
contains it’s own `create` method for creating new system users. This is a
|
||
essentially a wrapper around `User::create()` that automatically sets the value
|
||
of the system user field as part of the creation.
|
||
|
||
The original intention is that system users would always be created manually in
|
||
an custom install or update hook, however since releasing the module, I’ve also
|
||
added an install hook to the module to automatically create a new system user
|
||
when the module is installed, basing the username on the site name.
|
||
|
||
There is also an open issue to add a Drush command to create a new system user,
|
||
and I’d imagine I’ll also add a Drupal Console command too.
|
||
|
||
### Retrieving system users
|
||
|
||
Whilst you could easily write your own query that retrieves users based on the
|
||
value of the system user field, but the module contains a `SystemUserManager`
|
||
service that contains methods to do so. It also provides a static helper class
|
||
that determines if a specified user is a system user by checking the value of
|
||
the system user field.
|
||
|
||
```
|
||
// Retrieve the first system user.
|
||
$system_user = $this->systemUserManager->getFirst();
|
||
|
||
// Is the specified user a system user?
|
||
$is_system_user = SystemUserManager::isSystemUser($user);
|
||
```
|
||
|
||
But what do we return if there are no system users? You could return `NULL` or
|
||
`FALSE`, but I decided to take a different approach, which became the second
|
||
module.
|
||
|
||
## Null users
|
||
|
||
The [Null User module][2] is an implementation of the [null object pattern][3]
|
||
for users in Drupal 8. In this case, a [NullUser][4] is an extension of Drupal’s
|
||
`AnonymousUserSession`, which means that it inherits sensible defaults to return
|
||
for a non-existent User. Though, through inheritance, the `id`, `getRoles` and
|
||
`hasPermission` methods are overridden to return relevant values.
|
||
|
||
```php
|
||
use Drupal\Core\Session\AnonymousUserSession;
|
||
|
||
class NullUser extends AnonymousUserSession {
|
||
...
|
||
}
|
||
```
|
||
|
||
Null User module is a dependency of System User in Drupal 8, so When no system
|
||
user is found from the `getFirst()` method, a `NullUser` is returned. Whilst I
|
||
could alternatively have returned `NULL` or `FALSE`, we then would need to check
|
||
if the returned value was an object or not before calling methods on it.
|
||
|
||
```php
|
||
$system_user = $this->systemUserManager->getFirst(); // Returns NULL or FALSE.
|
||
|
||
// Need to check if a user was returned or not.
|
||
if (!$system_user) {
|
||
return;
|
||
}
|
||
|
||
if ($system_user->isActive()) {
|
||
...
|
||
}
|
||
```
|
||
|
||
Because instead we’re returning a `NullUser`, which through class inheritance
|
||
has the same methods and properties as a regular user, there is no need to do
|
||
the additional check as you will always receive a relevant object, and the
|
||
expected methods will always be present.
|
||
|
||
```php
|
||
$system_user = $this->systemUserManager->getFirst(); // Returns a NullUser.
|
||
|
||
if ($system_user->isActive()) {
|
||
...
|
||
}
|
||
```
|
||
|
||
This means we have less code, which also is simpler and more readable.
|
||
|
||
System User module is the only one that I’m aware of that makes use of Null
|
||
User, but I’ve added a list to the [project page][2] so let me know if you can
|
||
think of any others.
|
||
|
||
## Resources
|
||
|
||
- [Null object pattern][3]
|
||
- [Null User module][2]
|
||
- [System User module][1]
|
||
|
||
[1]: https://www.drupal.org/project/system_user
|
||
[2]: https://www.drupal.org/project/null_user
|
||
[3]: https://en.wikipedia.org/wiki/Null_object_pattern
|
||
[4]: http://cgit.drupalcode.org/null_user/tree/src/NullUser.php?h=8.x-1.x
|