# apostrophe-users
# Inherits from: apostrophe-pieces
# apos.users
The apostrophe-users
module provides user accounts. It is not intended to
be extended with new subclass modules. The apostrophe-login
module only
looks for instances of apostrophe-user
. Of course you may implicitly subclass
it at project level (not changing the name) in order to alter its behavior.
A user's permissions are determined by their membership in groups. See the
join with apostrophe-group
in the schema.
Groups are managed by the apostrophe-groups
module.
There is also a simplified permissions model in which you just specify
an array of groups
as an option to apostrophe-users
, and a single-select
dropdown menu allows you to pick one and only one of those groups for each user.
The recommended properties of each group in the array are title
,
slug
, and permissions
, which is an array of permission names such as
guest
, edit
and admin
. If you specify the groups
option when
configuring apostrophe-users
, the admin interface for
apostrophe-groups
will hide itself. Specifying slug
is optional,
however if your site has many documents there will be a startup time
penalty when specifying only title
due to the lack of indexing
on that property. For most larger sites we recommend not using the
groups
option at all; just manage groups via the admin bar.
# Public "staff directories" vs. users
In our experience, combining the concept of a "user" who can log in and do things
with the concept of a "staff member" who appears in a staff directory is more
trouble than it is worth. That's why the published
field is not present in
apostrophe-users
. You can add it back in, but then you have to deal with
the confusing concept of "users" who shouldn't actually be allowed to log in.
So for a staff directory, we suggest you create a separate employee
module
or similar, extending apostrophe-pieces
, unless it's true that basically
everyone should be allowed to log in.
# secrets
option
For security the password
property is not stored as plaintext and
is not kept in the aposDocs collection. Instead, it is hashed and salted
using the credential
module and the resulting hash is stored
in a separate aposUsersSafe
collection.
Additional secrets may be hashed in this way. If you set the
secrets
option to an array of property names, those properties
are never stored directly to the database. Instead, only their
hashes are stored, and only in aposUsersSafe
.
You may also call apos.users.addSecret('name')
to add a new
secret property. This is convenient when implementing a module
such as apostrophe-signup
.
# disableInactiveAccounts
option
If set to true, users are disabled after 90 days of inactivity. Users from the "admin" group are ignored. Default values can be changed:
neverDisabledGroups
must be an array of group names that should NOT be disabled due to not having logged in recently, like[ 'admin' ]
.inactivityDuration
must be an integer number of days. Users who have not logged in in more thaninactivityDuration
days will not be permitted to log in again until an admin clears thedisabled
flag via "Manage Users."
# Methods
# addOurTrashPrefixFields()
Add username
and email
to the list of fields that automatically get uniquely prefixed
when a user is in the trash, so that they can be reused by another piece. When
the piece is rescued from the trash the prefix is removed again, unless the username
or email address has been claimed by another user in the meanwhile.
# enableSecrets()
See options.secrets
and also the addSecret
method. enableSecrets
is part of the implementation and should not be called directly.
# ensureSafe(callback)
Index and obtain access to the aposUsersSafe
MongoDB collection as self.safe
.
# ensureSafeCollection(callback)
Obtain the aposUsersSafe
MongoDB collection as self.safe
.
# ensureSafeIndexes(callback)
Index the safe.
# afterConvert(req, piece, callback)
After a user is updated, check to see if the groups
option is configured for
simplified user management. If it is, convert the single-select choice made
via piece.group
to an array stored in groupIds
, so that all other code
can find groups in a consistent way.
# docBeforeInsert(req, doc, options, callback)
For security, on ANY insert of a doc, we check to see if it is
an apostrophe-user
and, if so, hash the password, remove it from the doc
and store the hash in the safe instead.
This method also checks password rules if they are in force and local logins are enabled.
# docBeforeUpdate(req, doc, options, callback)
For security, on ANY update of a doc, we check to see if it is
an apostrophe-user
and, if so, hash the password, remove it from the doc
and store the hash in the safe instead.
# insertOrUpdateSafe(req, doc, action, callback)
Insert or update a user's record in the safe, which stores the
password hash completely outside of the aposDocs
collection.
First checks to be sure this is an apostrophe-user
and invokes
its callback immediately if not. Invoked by docBeforeInsert
and docBeforeUpdate
.
# hashPassword(doc, safeUser, callback)
Hash the password
property of doc
, then delete that property
and update the passwordHash
property of safeUser
. This method is
called by the docBeforeInsert
and docBeforeSave handlers of this module. If
passwordis falsy (i.e. the user left it blank, requesting no change), it is left alone and
safeUser` is
not updated.
# hashSecrets(doc, safeUser, callback)
Similar to hashPassword
, this method hashes all of the properties
enumerated in options.secrets
and via addSecrets
, then deletes them
and updates the corresponding properties of safeUser
. If
a secret is named signup
, the corresponding property in
safeUser
will be named signupHash
.
This method is called by the docBeforeInsert
and docBeforeSave
handlers of this module.
# addSecret(name)
Add the property specified by name
to a list of
secret properties. These are never stored directly
to the user's doc in mongodb. Instead, if any of
them have non-falsy values at the time a user is saved,
those values are hashed and the hash is recorded
in a separate mongodb collection used only for this purpose.
You may then call verifySecret
later to verify that
a newly entered value matches the previously hashed
value. This is useful to verify password reset codes,
signup verification codes and the like with security
just as good as that used for the password.
# hashSecret(doc, safeUser, secret, callback)
Hashes a secret property of doc
, deletes the property,
and stores only the hash in safeUser
. secret
is
the name of the property of doc
, not the secret itself.
If secret
is the string 'password'
, then the password
property will be deleted from doc
and the passwordHash
property of safeUser
will be set.
If the secret property is falsy (i.e. the user left the
password field blank, requesting no change), it is left
alone and safeUser
is not updated.
Called automatically by hashSecrets
, above.
# verifySecret(user, secret, attempt, callback)
Check whether the provided value attempt
matches
the hash of the secret property secret
. For security
the user's password and other property names specified
in options.secrets
when configuring this module or via
addSecrets
are not stored as plaintext and are not kept in the
aposDocs collection. Instead, they are hashed and salted using the
credential
module and the resulting hash is stored
in a separate aposUsersSafe
collection. This method
can be used to verify that attempt
matches the
previously hashed value for the property named secret
,
without ever storing the actual value of the secret.
If no callback is passed, a promise is returned. If the verification fails the promise is rejected.
# forgetSecret(user, secret, callback)
Forget the secret associated with the property name
passed in secret
. If secret
is 'passwordReset'
,
then the passwordResetHash
property is deleted from
the appropriate record in the aposUsersSafe
collection. Note that the plaintext of the secret
was never stored in the database in the first place.
If no callback is passed, a promise is returned.
# deduplicateTrash(req, piece, callback)
Reflect email and username changes in the safe after deduplicating in the piece
# deduplicateRescue(req, piece, callback)
Reflect email and username changes in the safe after deduplicating in the piece
# ensureGroups(callback)
Ensure the existence of the groups configured via the groups
option,
if any, and refresh their permissions.
# ensureGroup(group, callback)
Create and/or refresh a group as specified by the
groups
option. The group is the second argument
to the callback.
# requirePiece(req, res, next)
Extend the standard middleware for the piece-editing routes
so that the group
single-select property is automatically set
to the id of the first group in groupIds
. This allows the single-select
dropdown element to work with data that actually lives in an array.
# initializeCredential()
Initialize the credential (opens new window) module.
# addFromTask(callback)
Implement the apostrophe-users:add
command line task.
# changePasswordFromTask(callback)
Implement the apostrophe-users:change-password
task.