Problem/Motivation
Right now, the source database credentials provided to begin an upgrade process are persisted by saving them to every migration configuration entity (as part of the 'source' key) created from the templates. #2455573: Support migration groups would at least save them to one central place, the group, but storing the credentials to a Drupal database in plain text in configuration is... sub-optimal.
Some options:
- The approach we used in Drupal 7 - encrypt the credentials and store them in configuration.
- Advantages - simplicity, keeping the configuration entity self-contained (not needing to reach outside itself to figure out how to get its data).
- Disadvantages - on general principle, keeping one database's credentials in another database (even encrypted) seems risky. A D7 issue with using drupal_private_key is when deploying the developed migration to another environment, the credentials can not be decrypted without changing that environment's drupal_private_key to that of the original environment.
- Don't persist the credentials - provide them explicitly through the front end for every operation (the original migrate-manifest implementation).
- Advantages - credentials not persisted, no worries about them getting stolen from the environment.
- Disadvantages - bad UX unless you're doing a single one-time migration, can't do things like migrate-status without having to re-enter the credentials.
- Store credentials in state. Whether deployability of the source database credentials is a good or bad thing may differ from project to project.
- Advantages - the credentials would not be deployable with configuration.
- Disadvantages - the credentials would not be deployable with configuration.
- Add to settings.php, as the install process does for the host Drupal database credentials.
- Advantages - same level of security as host Drupal database credentials.
- Disadvantages - writing settings.php is gross.
Proposed resolution
?
Remaining tasks
Figure out both an optimal solution for migrate_upgrade, and general best practices for any migration handling potentially sensitive source data credentials.
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #14 | interdiff.txt | 3.64 KB | mikeryan |
| #14 | how_best_to_persist-2548977-14.patch | 11.86 KB | mikeryan |
Comments
Comment #2
webchickI'm going to raise this to a migrate critical, since it seems like it's required prior to calling Migrate "supported" in D8 core.
Comment #3
mikeryanComment #4
mikeryanComment #5
mikeryanMoving to core queue - establishing best practices is a general migration system issue, and there may be changes to the core API depending on what we decide here.
Comment #6
phenaproximaRight off the bat I would rule out options #2 and #4. The UX of #2 is so abysmal that most users will probably hate us, and writing to settings.php is not merely gross, but super-annoying and potentially dangerous (permissions issues). In my mind, only #1 and #3 are viable options.
On balance, #3 (state) seems like the best option to me. It cannot be persisted across environments, true, but at least it won't be stored in plain text for all to see. I don't think it's much to ask for people to re-enter their DB credentials when switching between environments -- they only have to do it once per environment and there's a good chance that, most of the time, their credentials will differ across environments anyway. I think state offers us the best balance between UX and (such as it is) security concerns, and I'd be willing to sacrifice portability for those.
Comment #7
benjy commented#3 isn't a bad option although we still end up with plain text credentials in the database.
I actually think a combination of #2 and #4 could be great. Eg, you can enter the details in the form, but, if the user has put an entry in their settings file then that takes precedence. We don't write to settings.php, they can simply be put there by a developer? That works well for developers who often have settings.local.php files anyway per environment.
Comment #8
mikeryanI started playing with this a couple of weeks ago, then got distracted by other things (and apparently managed to blow away my feature branch). Anyway, what I was playing with was
In the lost branch this was basically working, the challenge was getting the tests working without the default key of 'migrate'...
Comment #9
phenaproximaThis is where we start to intersect with #2552791: MigrateSqlSource should use dependency injection. It should be Migrate Upgrade's job to persist the source DB credentials as it sees fit, and it should be the thing responsible for passing a fully initialized Connection object to the source plugins. Ideally, I'd like to see all of this done in the same patch.
Comment #10
mikeryanHere's an implementation of my proposal - the migrate_upgrade patch that uses this is at https://www.drupal.org/node/2574337#comment-10368365 (the tools in migrate_plus need no changes to work with it).
Comment #11
phenaproximaAre we going to have a follow-up issue for injecting this dependency?
Just for brevity's sake, can these use ternary syntax instead of if/else blocks?
Ditto here.
Actually, these two code blocks (using state or $this->configuration) are awfully similar -- seems to me they could be refactored into a single method.
Comment #12
mikeryanComment #13
phenaproxima\Drupal::state() should be $container->get('state').
Needs a type hint and documentation.
Should be type hinted.
$container->get('state')
Er...should be getMock() :)
Comment #14
mikeryanComment #15
phenaproximaHot diggity daffodil! This looks great to me. Presuming the PostrgeSQL and SQLite bots approve, methinks this is RTBC.
Comment #16
webchickGreat work. This solution indeed seems inline with how we handle sensitive data in other parts of the system.
Committed and pushed to 8.0.x. Thanks!
Comment #19
jeroent