-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Limited permission API keys #667
Conversation
{ | ||
"api_keys": [{ | ||
"permissions": { | ||
"/customers": "rw", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make more since to make this resource base rather then path based, since you could potently create a debit using /cards/asdf/debits or /customers/asdf/debits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something like
{
"permissions": {
"customers.index": "r",
"customers.CU123123": "w",
"cards.create": "w"
}
}
?
i'd like to see some examples on the syntax since i think the above example is not complete.
i feel like the path way is quite straight forward since i could do { "permissions": "/customers/CU123*" }
to allow all operations on a customer (debit, credit, associate a funding instrument).
@matthewfl did some more thinking about path based / endpoint based permissions.
these aren't exhaustive objections, just some points that i came up with and haven't been able to cleanly answer. I had a look at how other companies use scopes; github, google, heroku, linkedin. it looks like most of them either pass straight strings the oauth spec is not super helpful but it does say:
since this isn't a straight oauth impl i'll probably ignore that but i could see the scopes changing in an oauth impl to |
@mjallday I feel like you might consider looking at iam, since imo that is the most advance scope based auth system. an idea that I had recently was to use regular expressions and match against a string representing the operation. eg |
@mjallday @matthewfl arent' scopes already part of the balanced api? |
@mahmoudimus I believe that the scope implementation is more limited then what @mjallday is trying to do, eg there is a role, and some permissions associated with that role |
regex or glob is pretty much equiv. can be either or. do you need to differentiate between a POST a PUT, DELETE and a PATCH? your proposal would have four separate permissions whereas the existing treats them all as a write. do I need to also specify HEAD as well as GET? @mahmoudimus i looked at those scopes, not sure how we'd create arbitrary ones on the fly and how we'd use them to limit/deny writes. they appear to limit the scope of resources available to operate on but that's as far as they go. wrt iam policies, here's an example {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListAllMyBuckets"],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::bucket-name"
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
} i'm definitely not opposed to looking into these, they allow for the concept of roles which is not supported by the current implementation, each key needs to have permissions explicitly assigned. without trying to increase the scope (ha) of this pull-request it would be great to explore and identify any way we could tweak the implementation so that it's more forwards compatible. |
@mjallday I just suggested regexes since I feel like any type of permissions situation could be satisfied with them. Also I think you should differentiate between a POST and a PUT/PATCH since you can't move money with PUT/PATCH requests |
@matthewfl voiding a hold right? |
PUT to create would put a dent in that idea. Can accomplish something similar with this example which will not allow creates but still suffers from PUT to creates being allowed since you would specify the guid on creation {
"permissions": {
"/debits": "r", /* equiv to deny POST */
"/debits*" "w" /* equiv to allow PUT */
}
} |
Looking at the IAM example, we could restructure the permissions slightly to make them more future compatible with the syntax but i'd push back against actually implementing any additional functionality. e.g. {
"scopes": [
{
"path": "/debits",
"permission": ["read", "write"]
}
]
} if we decide to add deny actions, etc this format would make adding that much easier. |
@mahmoudimus voiding holds doesn't move money 😉 |
…subject matter that something deals with or to which it is relevant.
} | ||
}, | ||
"scopes": { | ||
"type": "object" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mjallday: Can you define the schema for scopes
?
A few questions:
|
|
@matin updated with a clearer error message that it's this particular API key that does not have permissions to perform that operation. the generic message is "Not permitted to perform create on debits." for the limited key it is "Not permitted to perform create on debits with this API key." lmk if you'd like that further clarified in the error message. |
@balanced/spec-ialz any more feedback so I can move this forward? |
lgtm. My 2¢, I think users will want the ability to add a description to the key so they can keep better track of them unless we update the Dashboard to show the key permissions, perhaps in a key detail view. |
@remear does the meta field solve that issue? |
@mjallday I think so, at least for now. |
I think users can optionally add |
sure, i don't see what benefit having a first class field for storing this sort of data provides. unless we want to be able to add constraints to or sort by it. meta still provides the ability to filter. i'd like to keep that out of scope and keep this pull-request focussed on a single enhancement. |
Sounds great to me. Ship it! |
When will this be merged in? |
@kyungmin i've had a couple of customers request this feature recently. it's useful for giving accountants ro access to view data etc. this feature is ready to go imo so there's no development left unless you need additional features. |
I'm the Zack from that irc log. Just piping in to mention that we think this is extremely important and near critical to our effective use of Balanced. Would really like to see this merged! |
Following up on this again. There's a horror story of leaked AWS keys making the rounds right now - their unrestricted keys ended up leaking in one way or another, racking up significant AWS bills when they were scraped and used by unscrupulous types. These instances are user error for not using IAM, but at least AWS supports it. The equivalent of this issue with a Balanced key could destroy a business. Consider a dev accidentally checking in an API key and it falling into inappropriate hands. Or any number of other ways a key could leak. Suddenly every credit card we have ever tokenized gets charged a maximum amount and the freshly topped up balance of our account is withdrawn to a newly tokenized bank account. This is flat out dangerous as it is; security is dependent on numerous developers "carefully storing" keys. Obviously we all want to be careful, but mistakes and/or security holes happen. Restricting keys to certain actions would provide a significant layer of defense. |
hey @zacksinclair we hear you :) we'll have something on this front soon. |
@mahmoudimus and i were talking last night. he was unhappy with the suggestion that we apply permissions directly to api keys. I countered that we could provide roles to make this simpler to manage {
"roles": [{
"name": "read-only",
"scopes": [
{
"path": "/*",
"permission": ["read"]
}
]
}]
} {
"api_keys": [{
...
"roles": ["read-only"]
}]
} This however will still proliferate roles if you attempt to create a key per customer (e.g. let my customer read what data i have stored for them, let me create a merchant dashboard) What could simplify that is if the role was not path based but rather used some other mechanism to describe the permissions. e.g. {
"role": [{
"name": "show-customer",
"resources": ["balanced:customer:::CU123123"],
"actions": ["customer.show", "customer.update"]
}]
} Where for a permission we have "customer.index" which is a combination of resource ("customer", "debit", "credit"... ) and action ("index", "update", "create", "delete", "show"). This is close to the AWS IAM permission {
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::bucket-name/*"
} We could allow overriding the "resources" attribute on the api key itself which would allow this role to be specified once and then applied to many api keys. @mahmoudimus if you can clarify if you think this would suit the issue that would be great. I initially explored this model but moved away from it because I did not know how to express something such as "allow this customer to update some limited subset of resources that belong to them". I think this particular implementation covers it (tho not sure if we would expose such functionality immediately). I suspect just providing a RO role as a first step would get us 90% of the way there. |
+1 @mjallday this is on the right track. |
{
"role": [{
"name": "show-customer",
"resources": ["balanced:customer:::CU123123"],
"actions": ["customer.show", "customer.update"]
}]
} How would you express the read-only mode in this format? Would it be |
@kyungmin yes, *.show would effectively be read-only. to begin with we'll have two pre-set roles for each marketplace, "read" and "read-write". a subsequent modification will allow creating custom roles but in the interest of speed we will not allow creation of custom roles. |
#667 (comment) is about only allowing conducting charges. Would something like |
This is a work in progress for #290 and #402
API keys that can be constrained to either read or write to a limited sub-set of endpoints.
E.g.
A POST to
/api_keys
will create an API key that can create customers and read all debits whose ID starts with
WD
.Note you can use asterisks as wildcards. URLS are matched exactly if a asterisk is not used so
/customers/CU123
does not match a permission of{"/customers": "r"}
but it does match{"/customers*": "r"}
This does not implement OAuth as suggested in the original issue, this implementation seemed simpler for now but could be pushed in that direction once an MVP is out and working. I think this would be very handy for passing read only credentials for 3rd party integrations or auditors etc.
Internal implementation exists at https://github.com/balanced/balanced/issues/570 and https://github.com/balanced/balanced/pull/622