Referring to a user by several possible identities - api-design

My application keeps multiple profile attributes for its users, such as:
An internal userId
Their phone number
Their email
etc. Each attribute is unique to a user; they can all be used as identity information.
I am designing an API with operations that refer to a specific user, eg charge.
I want to allow clients to identify users by any of the available profile attributes. In my specific domain, it is not possible to just enforce clients to use the internal userId, even if they can receive it in a separate call (eg getUserIdFromProfileAttribute).
Assuming the charge operation, it is a POST request with a JSON document inside the body. What would be the best way to identify the users? I am thinking one of the following:
Top-level key/value pairs for both the id and the id type:
{
"userId": <id>,
"userIdType": <idType>
}
Nested key/value pairs inside a user key:
{
"user": {
"id": <id>,
"type": <idType>
}
}
Single key/value pair, using a URI format with (possibly) custom protocols:
{
"user": <uri> # eg id:1234, tel:+19283912000, email:user#mail.com
}
Single key/value pair, using different keys for each id (one key per call):
{
"userId": <id> *OR*
"userMsisdn": <msisdn> *OR*
"userEmail": <email>
}
Same as above, but nested inside a user key:
{
"user": {
"id": <id> *OR*
"msisdn": <msisdn>
}
}
Any suggestions about best practices? Anyone can point me to some standard / widely used APIs with a similar need?
I should repeat that using just the internal userId in all calls is not possible, and using a separate call for each id (eg chargeById, chargeByEmail) is not practical as there are many such calls.

Related

Hasura object permission based authorization

I am trying to set a "Row Select" permissions on Hasura. I have a (simplified for brevity) Data Model like below
User
id: UserID
App
id: AppID
App Permissions
user_id: User ID
app_id: App ID
permissions: [ ENUM: Admin, View, Owner ]
Feed
app_id: AppID
feed_data: Some Feed Data
Now, I wish to query all Feed for an authenticated user. The query can be of the form
GET all apps, for which the authenticated user has view permissions
query MyQuery {
feed(limit: 10) {
app_id
feed_data
}
}
GET apps with app_id in the query filter for which the authenticated user has view permissions
query MyQuery {
feed(limit: 10, where: {app_id: {_in: [1, 2]}}) {
app_id
feed_data
}
}
Since feed table does not have user_id information directly in it, I can not use X-Hasura-User-Id attribute directly against feed table. I also tried to use _exists relation against the app_permission table, but I am unable to put app_id filter in the permission clause.
{
"_exists": {
"_where": {
"user_id": {
"_eq": "X-Hasura-User-Id"
}
},
"_table": {
"schema": "public",
"name": "app_permission"
}
}
}
I am not really sure how to proceed with such data modelling with Hasura. Any help is appreciated. Thanks.
Since you dont have a direct relationship, I think you can query via appPermissions Table instead of directly querying feeds table.
When you create a feeds table with appId as foreign key relationship, Hasura lets you track this relationship as shown below
This way you can make nested graphQL queries to appPerms table as shown below
query GetUserFeeds {
test_appPerms {
id
userId
feeds(limit: 10) {
app_id
id
feed_data
}
}
}
Another thing I'd like to suggest is that you could try is by using a session variable like x-hasura-app-id along side a x-hasura-role and build your permissions around that.
https://hasura.io/docs/latest/graphql/core/auth/authorization/roles-variables.html

Provider to ignore some date and identifier fileds

New to pact, but excited to implement at my current 'micro services' based company
I generated some reports that compares JSON responses, all done in ruby.
But I am getting stumped by a few keys that are date based and index based.
Key: - is expected
+ is actual
Matching keys and values are not shown
{
"metadata": {
- "received_at": "2017-10-23T11:50:12Z"
+ "received_at": "2017-10-25T01:26:00Z"
},
"response": {
"cascading_avm_results": {
"automated_valuation": {
- "run_date": "2017-10-23",
- "internal_run_identifier": "1508784611820479",
- "valuation_date": "2017-10-23"
+ "run_date": "2017-10-25",
+ "internal_run_identifier": "1508963160085440",
+ "valuation_date": "2017-10-25"
}
}
}
is there a way to make the provider response ignore it, or modify the output to match those keys?
My understanding, and please correct me if I am wrong, is that we shouldn't stub that provider response on the contract testing right?
What you want to do is match the shape of the response, and not specific values. For that you'll need to use flexible matchers - https://github.com/realestate-com-au/pact/wiki/Regular-expressions-and-type-matching-with-Pact.
You can match based on regular expressions, value types etc., including within arrays.

Updating a string in multiple locations in firebase

I'm making an app with database structure like this:
{
"Locations": {
"location1": {
"name": "Nice location"
}
},
"User_posts": {
"user1": {
"post1": {
"location_name": "Nice location",
"location_id": "location1",
"description": "Wow!"
},
"post2": {
"location_name": "Nice location",
"location_id": "location1",
"description": "Nice"
}
}
}
If I have to change location1 name, how to change all location_name's that all users posts have? I have to download all the data before and update it or there is other method?
I think that using location id only to get location name for every location when user enters his posts is not a good idea.
By duplicating data you improve your read performance/scalability at the cost of decreased write performance. This is a normal trade-off in NoSQL databases and in highly scaleable systems in general.
If you want to update the location_name of all posts, you will indeed have to query the posts and update each. If you need to do this regularly, consider keeping a separate lookup list for each location to find the posts where it used. Such an inverted index is another common occurrence in NoSQL databases.
I covered strategies for updating the duplicated data in my answer here: How to write denormalized data in Firebase
Coming from a relational/SQL background, this may initially feel uncomfortable, since it goes against the normalization rules we've been taught. To counter that feeling, I recommend reading NoSQL data modeling, watching Firebase for SQL developers and in general just read some more NoSQL data modeling questions.
You can add one more attribute to location1 , say isLocationOf , which will store all the user id or perhaps post id/post names. Like
"Locations": {
"location1": {
"name": "Nice location",
"isLocationOf": {
'post1': true,
'post2': true
}
}
}
Here isLocationOf is an attribute of Locations whose value is an object.Now if locations1's name gets changed then you can retrieve its isLocationOf object , iterate through it , get all posts id/name containing that location.Then use the post ids to update all entries having this address .
Also whenever you add new post , you have to add its post id/name to isLocation object.

How can I grant access to array of admins on Firebase database rules

I am stuck trying to allow an an array of admins access to their data.
I have a database structure like this:
{
"Respondents": {
"Acme Corp": {
"admins": ["mMK7eTrRL4UgVDh284HntNRETmx1", ""mx1TERNmMK7eTrRL4UgVDh284Hnt"],
"data": {data goes here...}
},
"Another Inc": {
"admins": ["Dh284HmMK7eTrRL4UgVDh284HntN", ""x1TERNmx1TERNmMK7eTrRL4UgVDh"],
"data": {their data goes here...}
}
}
}
And then I tried to set my rules like this
{
"rules": {
"Respondents": {
"$organisation" : {
".read": "root.child('Respondents').child($organisation).child('admins').val().includes(auth.id)",
".read": "root.child('Respondents').child($organisation).child('admins').val().includes(auth.id)"
}
}
}
}
..but that won't parse in the Firebase Database Rules editor
I get "Error saving rules - Line 7: No such method/property 'includes'", but I need something to match the user id with the array of admins.
Any experience or suggestions?
As you've found, there is no includes() operation in Firebase's security rules. This is because Firebase doesn't actually store the data as an array. If you look in the Firebase Database console or read this blog post you will see that Firebase stores it as a regular object:
"admins": {
"0": "mMK7eTrRL4UgVDh284HntNRETmx1",
"1": "mx1TERNmMK7eTrRL4UgVDh284Hnt"
}
And since that is a regular JavaScript object, there is no contains() method on it.
In general creating arrays are an anti-pattern in the Firebase Database. They're often the wrong data structure and when used are regularly the main cause of scalability problems.
In this case: you're not really looking to store a sequence of UIDs. In fact: the order of the UIDs doesn't matter, and each UID can be meaningfully present in the collection at most once. So instead of an array, you're looking to store set of uids.
To implement a set in Firebase, you use this structure:
"admins": {
"mMK7eTrRL4UgVDh284HntNRETmx1": true,
"mx1TERNmMK7eTrRL4UgVDh284Hnt": true
}
The value doesn't matter much. But since you must have a value to store a key, it is idiomatic to use true.
Now you can test whether a key with the relevant UID exists under admins (instead of checking whether it contains a value):
"root.child('Respondents').child($organisation).child('admins').child(auth.uid).exists()",

Angularfire generate new unique ID for non-email object

I'm making a web app using angularfire. I have a url for users at 'url.firebaseio.com/users'. I want to make another url to store chat-room type things at 'url.firebaseio.com/rooms'. When I create a new user using the $createUser() method, it stores the user's information under a unique ID created by $createUser() in the '/users' url. I want to do this with the new '/rooms' url, but I can't find a way to generate unique IDs in the same way without $createUser(). I can't use $createUser() because it requires an email address argument, and I just want to take in a name for the room and a password, all in an object under the unique ID for the room.
I can't think of any code to provide, so here's what a user looks like:
users: {
uniqueUserId: {
email: email#email.com,
name: name
}
}
And here's what I'd like a 'room' to look like:
rooms: {
uniqueRoomId: {
roomName: something
}
}
Is there a built-in way to do this? If not would it be best to generate IDs on my own?

Resources