Backend API for Identity Provider Password Management
- Docker >= 1.9.1
- Docker Compose >= 1.5
- Docker for Mac
- Clone this repo
- Copy
local.env.dist
tolocal.env
andemail.local.env.dist
toemail.local.env
and update values in each as appropriate. - Setup environment variable for
DOCKER_UIDGID
in 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_profile
file). - Setup environment variable for
COMPOSER_CONFIG_FILE
with 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.dist
toapplication/common/config/local.php
and 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:8080.
- You'll probably also want the web interface for this application which you can clone at https://github.com/silinternational/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 AppConfig.
To do this, set the following environment variables to point to the configuration in
AWS:
AWS_REGION
- the AWS region in useAPP_ID
- the application ID or nameCONFIG_ID
- the configuration profile ID or nameENV_ID
- the environment ID or name
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.
The content of the configuration profile takes the form of a typical .env file, using
#
for comments and =
for variable assignment. Any variables read from AppConfig
will overwrite variables set in the execution environment.
- Add entry to
/etc/hosts
for127.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 start
will 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 G Suite 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.dist
file 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' => '[email protected]',
// 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' => 'G Suite',
]);
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 and to the email service.
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 | Local, Docker | GitHub Actions |
---|---|---|---|---|
Unit | PHPUnit | container | unittest | api |
script | run-tests.sh | (same) | ||
env. | common.env, test.env | actions-services.yml | ||
bootstrap | tests/_bootstrap.php | (same) | ||
config | tests/unit.suite.yml, tests/codeception/config/unit.php | (same) | ||
coverage | IdBroker, IdBrokerPw, Ldap | (same) | ||
------- | ------------- | ----------- | ---------------------- | ----------------------- |
Unit | Behat | container | unittest | api |
script | run-tests.sh | (same) | ||
env. | common.env, test.env | actions-services.yml | ||
bootstrap | Composer | (same) | ||
config | features/behat.yml | (same) | ||
coverage | Multiple, Google | (same) | ||
------- | ------------- | ----------- | ---------------------- | ----------------------- |
Unit | Codeception | container | unittest | api |
script | run-tests.sh | (same) | ||
env. | common.env, test.env | actions-services.yml | ||
bootstrap | tests/_bootstrap.php | (same) | ||
config | tests/unit.suite.yml | (same) | ||
coverage | models, helpers | (same) | ||
------- | ------------- | ----------- | ---------------------- | ----------------------- |
API | Codeception | container | apitest | api |
script | run-tests-api.sh | (same) | ||
env. | common.env, test.env | actions-services.yml | ||
bootstrap | tests/_bootstrap.php | (same) | ||
config | tests/api.suite.yml | (same) | ||
coverage | controllers | (same) | ||
------- | ------------- | ----------- | ---------------------- | ----------------------- |
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