Backend API for Identity Provider Password Management
- Docker
- Docker Compose
- Docker for Mac
- Clone this repo
- Copy
local.env.disttolocal.envand update values as appropriate. - Setup environment variable for
DOCKER_UIDGIDin the format of"uid:gid". This will run some of the containers as you so that they can write to your host filesystem and the file permissions will be owned by you. On Mac (and possibly other *nix-based systems), this can be done by running this:export DOCKER_UIDGID="$(id -u):$(id -g)"(even in your.bash_profilefile). - Setup environment variable for
COMPOSER_CONFIG_FILEwith the full system path to your composer config.json file, for example:/home/my/.composer/config.json. This will allow the composer container to use your github auth token when pulling dependencies. - (OPTIONAL) Copy
application/common/config/local.php.disttoapplication/common/config/local.phpand update with appropriate settings - Follow operating system specific steps below
- You should be able to access the API using a REST client or your browser at http://idp-pw-api.local:51155.
- You'll probably also want the web interface for this application which you can clone at https://github.com/sil-org/idp-profile-ui
By default, configuration is read from environment variables. These are documented
in the local.env.dist file. Optionally, you can define configuration in AWS Parameter Store.
To do this, set the following environment variables to point to the configuration in AWS:
AWS_REGION- the AWS region in usePARAMETER_STORE_PATH- Parameter Store base path for this app, e.g. "/idp-pw-api/idp-name/prod"
In addition, the AWS API requires authentication. It is best to use an access role
such as an ECS Task Role.
If that is not an option, you can specify an access token using the AWS_ACCESS_KEY_ID and
AWS_SECRET_ACCESS_KEY variables.
If PARAMETER_STORE_PATH is given, AWS Parameter Store will be used. Each parameter in AWS Parameter
Store is set as an environment variable in the execution environment.
- Add entry to
/etc/hostsfor127.0.0.1 idp-pw-api.local - Run
docker build -t idp-pw-api . - Run
make start
To simplify common tasks there is a Makefile in place. The most common tasks will likely be:
make start- Does what is needed to get API server onlinemake test- Does cleanup and restart of test instances and runs local (unit and api) and integration testsmake testlocal- Does cleanup and restart of test instances and runs just the local testsmake testintegration- Runs just the integration testsmake clean- Remove all containersmake composerupdate-make startwill run acomposer install, but to update composer you need to runmake composerupdate
Note: The CI/CD process only runs the local tests now.
With the goal of being reusable, this application is developed with a component based architecture that allows swapping out specific components to suit your needs. All components must implement common interfaces to support this and new components can be developed to implement the interface as needed.
The interfaces for the following components are stored within the application/common/components source tree.
All components must extend
from \yii\base\Component so that they
can be configured in the components section of the application configuration. This also allows them to be accessed
via \Yii::$app->componentId. While each component has a defined interface for methods to implement, what
properties it needs for configuration are up to each implementation as appropriate.
See our common/config/local.php.dist
for examples of configurations.
We use SAML for authentication but this component can be replaced to support whatever method is needed. For example an auth component could be written to implement OAuth or use Google, etc.
- Component ID:
auth - Implement interface:
common\components\auth\AuthnInterface - Example implementation
You can store your passwords wherever you like, whether it is LDAP, Active Directory, a database, or even Redis.
- Component ID:
passwordstore - Implement interface:
common\components\passwordStore\PasswordStoreInterface - Example implementation
The personnel component is used to look up informaton about users from your company's personnel system. This includes verifying that they are an active employee, getting information about them like name, email, employee id, whether they have a supervisor and what their supervisors email address is.
- Component ID:
personnel - Implement interface:
common\components\personnel\PersonnelInterface - Example implementation
Password store component for IdP PW API that uses Google as the backend.
- Create a project on https://console.developers.google.com/.
- Still in the Google Developers Console, create a Service Account.
- Check "Furnish a new private key" and "Enable Google Workspace Domain-wide Delegation".
- Save the JSON file it provides (containing your private key), but DO NOT store it in public version control (such as in a public GitHub repo).
- Enable the "Admin SDK" API for your project in the Google Developers Console.
- Have an admin for the relevant Google Apps domain go to
http://admin.google.com/ and, under Security, Advanced, Manage API Client
Access, grant your Client ID access to the following scope:
https://www.googleapis.com/auth/admin.directory.user - Set up a delegated admin account in Google Apps, authorized to make changes to users. You will use that email address as the value for an env. var.
- See the
local.env.distfile to know what environment variables to provide when using this component.
$googlePasswordStore = new GooglePasswordStore([
// Required config fields (dummy values are shown here):
'applicationName' => 'Name of Your Application',
'delegatedAdminEmail' => 'some_admin@yourgoogledomain.com',
// You must provide one of these two fields:
'jsonAuthConfigBase64' => '...', // A base64-encoded string of the JSON
// auth file provided by Google.
'jsonAuthFilePath' => '/somewhere/in/your/filesystem/google-auth.json',
// Optional config fields (current defaults are shown here):
'emailFieldName' => 'email',
'employeeIdFieldName' => 'employee_id',
'userActiveRecordClass' => '\common\models\User',
'displayName' => 'Google Workspace',
]);
For details about what each of those fields is used for, see the documenting
comments in the /application/common/components/passwordStore/Google.php file.
If running the Google PasswordStore tests (which are integration tests), you
will need to provide credentials in the local.env file in the
TEST_GOOGLE_... variables for the values described above. See the
local.env.dist file for the variable names.
The API is described by api.raml, and an auto-generated api.html created by
raml2html. To regenerate the HTML file, run make raml2html.
To quickly get up and running to verify basic operation of the API, these are a few endpoints to start with. GET endpoints can be exercised with any browser, but others will need something like Insomnia.
Returns configuration parameters supplied by environment variables.
This endpoint verifies connectivity to the database.
This combination requires connection to a PasswordStore component and a Personnel
component containing a valid user record. After sending the POST, retrieve the reset
code from the email or the database, and the reset uid from the response body, then
supply them in the PUT request body and URI. The response will contain an
access_token to use for subsequent calls that require it.
This method of authentication will provide a full-scope access_token. The easiest
method is to use an invite code, which can be found in the ID Broker database after
creating a new user. The access_token can be found in the Location response header.
Tests are configured in multiple places, using different test frameworks. The chart below summarizes the test configuration.
| Suite | Framework | config | details |
|---|---|---|---|
| Unit | PHPUnit | container | unittest |
| script | run-tests.sh | ||
| env. | common.env, test.env | ||
| bootstrap | tests/_bootstrap.php | ||
| config | tests/unit.suite.yml, tests/codeception/config/unit.php | ||
| coverage | IdBroker, IdBrokerPw, Ldap | ||
| ------- | ------------- | ----------- | ---------------------- |
| Unit | Behat | container | unittest |
| script | run-tests.sh | ||
| env. | common.env, test.env | ||
| bootstrap | Composer | ||
| config | features/behat.yml | ||
| coverage | Multiple, Google | ||
| ------- | ------------- | ----------- | ---------------------- |
| Unit | Codeception | container | unittest |
| script | run-tests.sh | ||
| env. | common.env, test.env | ||
| bootstrap | tests/_bootstrap.php | ||
| config | tests/unit.suite.yml | ||
| coverage | models, helpers | ||
| ------- | ------------- | ----------- | ---------------------- |
| API | Codeception | container | apitest |
| script | run-tests-api.sh | ||
| env. | common.env, test.env | ||
| bootstrap | tests/_bootstrap.php | ||
| config | tests/api.suite.yml | ||
| coverage | controllers | ||
| ------- | ------------- | ----------- | ---------------------- |
To run all tests, use make test.
To run a single unit test:
docker compose run --rm unittest vendor/bin/codecept run tests/unit/common/models/PasswordTest.php:testBadBytes