device api endpoints #110
device api endpoints #110
Conversation
dee2a17
to
f32ba72
|
This needs some more tests but the main idea is done. I did some things in the sql that are a bit outside my comfort zone, so another look to make sure I'm not back asswords would be helpful. @rfk f? |
| ) | ||
| .done(next, next) | ||
| } | ||
| ) |
rfk
Oct 8, 2015
Member
There's an asymmetry here, updating a device record via POST /device but deleting it via DELETE /device/:deviceId. For other datatypes we've been relying on fxa-auth-server to generate the ids and pass them through to us in the URL, should we do the same with devices for consistency?
There's an asymmetry here, updating a device record via POST /device but deleting it via DELETE /device/:deviceId. For other datatypes we've been relying on fxa-auth-server to generate the ids and pass them through to us in the URL, should we do the same with devices for consistency?
rfk
Oct 8, 2015
Member
So having read further down, I see why you want to generate the ids internally here. It still feels a bit out-of-place given the rest of the API, but perhaps it's the only reasonable way to get the combination of short-enough-to-not-be-identifiable and guaranteed-not-to-collide-per-user.
We should carefully consider what we're guarding against by making the device-ids short though. If they only ever appear on the wire alongside a long-and-uniquely-identifiable sessionToken id, it may not provide much value.
So having read further down, I see why you want to generate the ids internally here. It still feels a bit out-of-place given the rest of the API, but perhaps it's the only reasonable way to get the combination of short-enough-to-not-be-identifiable and guaranteed-not-to-collide-per-user.
We should carefully consider what we're guarding against by making the device-ids short though. If they only ever appear on the wire alongside a long-and-uniquely-identifiable sessionToken id, it may not provide much value.
| ) | ||
| SELECT | ||
| inUid, | ||
| COALESCE(MAX(id), 0) + 1, |
rfk
Oct 8, 2015
Member
To clarify, the idea here is to to ensure that each user gets devices numbered 1, 2, 3 etc? I seem to recall MySQL having a feature that can do this with auto-increment columns if they're part of a compound primary key, which may be both more efficient and clearer. (That is to say, if you just declare the id column to be auto-increment, MySQL may magically do what you want).
To clarify, the idea here is to to ensure that each user gets devices numbered 1, 2, 3 etc? I seem to recall MySQL having a feature that can do this with auto-increment columns if they're part of a compound primary key, which may be both more efficient and clearer. (That is to say, if you just declare the id column to be auto-increment, MySQL may magically do what you want).
rfk
Oct 8, 2015
Member
Also, I reckon we'll get at least one security bug from someone freaking out about our use of "predictable identifiers in URLs".
Also, I reckon we'll get at least one security bug from someone freaking out about our use of "predictable identifiers in URLs".
dannycoates
Oct 8, 2015
Author
Member
MySQL may magically do what you want
It doesn't. There's all kinds of caveats, especially with innodb.
https://dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html
MySQL may magically do what you want
It doesn't. There's all kinds of caveats, especially with innodb.
https://dev.mysql.com/doc/refman/5.7/en/example-auto-increment.html
rfk
Oct 8, 2015
Member
Oh dear lord there's a whole section dedicated to “AUTO_INCREMENT Handling in InnoDB”...
Oh dear lord there's a whole section dedicated to “AUTO_INCREMENT Handling in InnoDB”...
| type VARCHAR(16) NOT NULL, | ||
| createdAt BIGINT UNSIGNED NOT NULL, | ||
| callbackURL VARCHAR(255), | ||
| INDEX devices_uid (uid), |
rfk
Oct 8, 2015
Member
You shouldn't need this index, it's a prefix of the primary key so it's already covered by the main table index.
You shouldn't need this index, it's a prefix of the primary key so it's already covered by the main table index.
| var device = devices[id] || {} | ||
| delete sessionTokens[device.sessionTokenId] | ||
| delete account.devices[device.id] | ||
| return {} |
rfk
Oct 8, 2015
Member
Should this also delete it from the global devices object?
Actually, it's not clear to me what that global devices object is for, since you should always have a reference to the account when dealing with a device.
Should this also delete it from the global devices object?
Actually, it's not clear to me what that global devices object is for, since you should always have a reference to the account when dealing with a device.
dannycoates
Oct 8, 2015
Author
Member
Actually
Yeah, this model is out of date from a previous design
Actually
Yeah, this model is out of date from a previous design
| function (err) { | ||
| return [] | ||
| } | ||
| ) |
rfk
Oct 8, 2015
Member
Clearly this changes the semantics of accountDevices from "list all the session tokens" to "list all the devices", which makes sense.
Seeing the UA information joined on here like this, I wonder if we should just create a device record for all sessions, put this information in that record directly, and remove it from the sessionToken table. At the least, we should ensure every login with service=sync receives a device record, even if it's from a client too old to register itself explicitly. Otherwise weird stuff will happen with e.g. the journeybuilder events.
If not, I think we should keep an API for listing all the sessionTokens on the account, we're going to need to do that sooner or later regardless of whether they qualify as devices or not.
Clearly this changes the semantics of accountDevices from "list all the session tokens" to "list all the devices", which makes sense.
Seeing the UA information joined on here like this, I wonder if we should just create a device record for all sessions, put this information in that record directly, and remove it from the sessionToken table. At the least, we should ensure every login with service=sync receives a device record, even if it's from a client too old to register itself explicitly. Otherwise weird stuff will happen with e.g. the journeybuilder events.
If not, I think we should keep an API for listing all the sessionTokens on the account, we're going to need to do that sooner or later regardless of whether they qualify as devices or not.
dannycoates
Oct 8, 2015
Author
Member
listing sessions is already another thing https://github.com/mozilla/fxa-auth-db-mysql/blob/master/lib/db/mysql.js#L317-L321
listing sessions is already another thing https://github.com/mozilla/fxa-auth-db-mysql/blob/master/lib/db/mysql.js#L317-L321
|
|
||
| CREATE TABLE devices ( | ||
| uid BINARY(16) NOT NULL, | ||
| id INT UNSIGNED NOT NULL, |
rfk
Oct 8, 2015
Member
We should consider making this smaller than INT, especially since it appears in the primary key. Anyone who registers 4294967295 devices has bigger problems than running out of device id space.
We should consider making this smaller than INT, especially since it appears in the primary key. Anyone who registers 4294967295 devices has bigger problems than running out of device id space.
dannycoates
Oct 8, 2015
Author
Member
I had it as SMALLINTbut changed it because we may want a device id for web sessions at some point and I'd rather never reuse an id* (attach detach attach detach) just in case. 65k is a lot but not well beyond my imagination for strange user behavior.
*Restrictions may apply. Integrity guarantees do not include data loss due to bad design, rodents, cosmic rays, power failure, earthquakes, flipped bits, flipped switches, alcohol, box jellyfish, kangaroos, spice girls, chemicals, climate change, apocalypse, or other acts of god.
I had it as SMALLINTbut changed it because we may want a device id for web sessions at some point and I'd rather never reuse an id* (attach detach attach detach) just in case. 65k is a lot but not well beyond my imagination for strange user behavior.
*Restrictions may apply. Integrity guarantees do not include data loss due to bad design, rodents, cosmic rays, power failure, earthquakes, flipped bits, flipped switches, alcohol, box jellyfish, kangaroos, spice girls, chemicals, climate change, apocalypse, or other acts of god.
rfk
Oct 8, 2015
Member
If you delete the device with the current MAX(id), won't that id be used again when you attach a subsequent device?
If you delete the device with the current MAX(id), won't that id be used again when you attach a subsequent device?
dannycoates
Oct 8, 2015
Author
Member
?
fixed disclaimer
?
fixed disclaimer
rfk
Oct 8, 2015
Member
We should run that by the lawyers to see if there's any we missed...
We should run that by the lawyers to see if there's any we missed...
|
Also /cc @philbooth for additional f? |
|
Reading this PR has been educational for me. I'm definitely not qualified to have an opinion on most of it but I'm going to post a couple of questions in-line, just for stuff I'm curious about. I notice the API docs aren't updated yet but that was probably on your final to-do list anyway. |
| AND | ||
| id = inId; | ||
|
|
||
| SELECT |
philbooth
Oct 8, 2015
Contributor
What's the reason for returning this from the update?
What's the reason for returning this from the update?
philbooth
Oct 8, 2015
Contributor
(other than consistency with the create I mean. I realise they behave the same for upsert sanity, but why does the upsert return the device?)
(other than consistency with the create I mean. I realise they behave the same for upsert sanity, but why does the upsert return the device?)
philbooth
Oct 8, 2015
Contributor
(oh wait, stupid question alert, is it because the id is auto-generated?)
(oh wait, stupid question alert, is it because the id is auto-generated?)
| ORDER BY | ||
| d.id | ||
| DESC | ||
| LIMIT 1; |
philbooth
Oct 8, 2015
Contributor
I notice these two queries aren't in a transaction. Is it possible (albeit unlikely) that a concurrent call to createDevice could cause the wrong device id to be returned here? If not, why not?
I notice these two queries aren't in a transaction. Is it possible (albeit unlikely) that a concurrent call to createDevice could cause the wrong device id to be returned here? If not, why not?
|
I thought the short device id would be a good idea and I think it was useful to see how it'd work, but I bet @rfk is right that a uuid will be the better option long term. The compromises don't really outweigh the possible benefits. |
b15e306
to
00bb3c1
| deviceInfo.name, | ||
| deviceInfo.type, | ||
| deviceInfo.createdAt, | ||
| deviceInfo.callbackURL |
rfk
Oct 20, 2015
Member
IIUC passing null or undefined in here will cause the COALESCE to leave the existing value unchanged. How would a client explicitly delete e.g. their callbackURL? Pass in the empty string?
IIUC passing null or undefined in here will cause the COALESCE to leave the existing value unchanged. How would a client explicitly delete e.g. their callbackURL? Pass in the empty string?
|
There's a lot of deletions in the shrinkwrap change, just want to sanity-check whether that's expected or whether it e.g. accidentally drops dev dependencies |
| sessionTokenId = COALESCE(inSessionTokenId, sessionTokenId), | ||
| name = COALESCE(inName, name), | ||
| type = COALESCE(inType, type), | ||
| callbackURL = COALESCE(inCallbackURL, callbackURL); |
rfk
Oct 20, 2015
Member
I think it would be worth a test that the ON DUPLICATE KEY UPDATE logic is correctly triggered here, e.g. doing two calls to upsertDevice and confirming that the selected keys are properly overridden.
I think it would be worth a test that the ON DUPLICATE KEY UPDATE logic is correctly triggered here, e.g. doing two calls to upsertDevice and confirming that the selected keys are properly overridden.
|
@dannycoates this is looking solid, I had a couple of little quibbles there but otherwise I'm r+ |
| api.put( | ||
| '/account/:id/device/:deviceId', | ||
| function (req, res, next) { | ||
| db.upsertDevice(req.params.uid, req.params.deviceId, req.body) |
vladikoff
Oct 20, 2015
Contributor
@rfk @dannycoates is there a way to integrate the Activity Events into this? Do we we have a way to tell that a new device was added and log device.added to Heka? (could be separate bug)
@rfk @dannycoates is there a way to integrate the Activity Events into this? Do we we have a way to tell that a new device was added and log device.added to Heka? (could be separate bug)
vladikoff
Oct 20, 2015
Contributor
I guess that logic would go to the fxa-auth-server pr?
I guess that logic would go to the fxa-auth-server pr?
rfk
Oct 20, 2015
Member
for completeness: yes we should do this, and yes it belongs in the corresponding auth-server PR; thanks @vladikoff
for completeness: yes we should do this, and yes it belongs in the corresponding auth-server PR; thanks @vladikoff
| "mozlog": "2.0.2", | ||
| "mysql": "2.7.0", | ||
| "request": "2.65.0", | ||
| "restify": "4.0.3" | ||
| }, | ||
| "devDependencies": { |
vladikoff
Oct 20, 2015
Contributor
npm-shrinkwrap.json
331 additions, 1,790 deletions not shown
Did you remove --dev from shrinkwrap?
npm-shrinkwrap.json
331 additions, 1,790 deletions not shown
Did you remove --dev from shrinkwrap?
dannycoates
Oct 20, 2015
Author
Member
yes, intentionally.
yes, intentionally.
| @@ -0,0 +1,131 @@ | |||
|
|
|||
vladikoff
Oct 20, 2015
Contributor
is this missing the lib/db/schema/patch-019-018.sql DB script?
is this missing the lib/db/schema/patch-019-018.sql DB script?
dannycoates
Oct 20, 2015
Author
Member
good catch
good catch
rfk
Oct 20, 2015
Member
/me would love a custom linter for these db migrations
/me would love a custom linter for these db migrations
|
shipit! |
new endpoints for the device api
(stealth edit by @rfk to link things in waffle: Fixes #104)