-
Notifications
You must be signed in to change notification settings - Fork 3
MANTA-5522 Add lib/scope-schema.js as the single source of truth for bucket scope schema #11
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
base: master
Are you sure you want to change the base?
Changes from all commits
5577741
62c2486
99b3889
dfc265e
ff9ed82
47d6052
0e112fa
d8f6826
b8d7800
d9eeb4d
ed502d8
5e85be3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ var LRU = require('lru-cache'); | |
| var httpSignature = require('http-signature'); | ||
| var qs = require('querystring'); | ||
| var restify = require('restify'); | ||
| var scopeSchema = require('./scope-schema.js'); | ||
| var sprintf = require('util').format; | ||
|
|
||
|
|
||
|
|
@@ -797,7 +798,7 @@ MahiClient.prototype.getLookup = function getLookup(opts, cb) { | |
| * | ||
| * accessKeyId: AWS access key ID (e.g., "AKIA123456789EXAMPLE") | ||
| * cb: callback in the form fn(err, userInfo) | ||
| * | ||
| * | ||
| * Returns user object without access key secrets: | ||
| * { | ||
| * type: "account", | ||
|
|
@@ -811,14 +812,14 @@ MahiClient.prototype.getLookup = function getLookup(opts, cb) { | |
| * AccessKeyNotFoundError | ||
| * RedisError | ||
| */ | ||
| MahiClient.prototype.getUserByAccessKey = function | ||
| MahiClient.prototype.getUserByAccessKey = function | ||
| getUserByAccessKey(accessKeyId, cb) { | ||
| assert.string(accessKeyId, 'accessKeyId'); | ||
| assert.func(cb, 'callback'); | ||
|
|
||
| var self = this; | ||
| var path = sprintf('/aws-auth/%s', accessKeyId); | ||
|
|
||
| self.http.get(path, function (err, req, res, obj) { | ||
| if (err) { | ||
| cb(err); | ||
|
|
@@ -831,42 +832,41 @@ MahiClient.prototype.getUserByAccessKey = function | |
|
|
||
| /** | ||
| * Verify AWS Signature Version 4 authentication | ||
| * | ||
| * | ||
| * request: HTTP request object with AWS4-HMAC-SHA256 authorization header | ||
| * cb: callback in the form fn(err, result) | ||
| * | ||
| * Returns: | ||
| * { | ||
| * valid: true, | ||
| * accessKeyId: "AKIA123456789EXAMPLE", | ||
| * accessKeyId: "AKIA123456789EXAMPLE", | ||
| * userUuid: "user-uuid" | ||
| * } | ||
| * | ||
| * errors: | ||
| * InvalidSignatureError | ||
| * AccessKeyNotFoundError | ||
| * AccessKeyNotFoundError | ||
| * RequestTimeTooSkewedError | ||
| */ | ||
| MahiClient.prototype.verifySigV4 = function verifySigV4(request, cb) { | ||
| assert.object(request, 'request'); | ||
| assert.func(cb, 'callback'); | ||
|
|
||
| var self = this; | ||
|
|
||
| // Forward the original headers to mahi for SigV4 verification | ||
| var requestOptions = { | ||
| path: '/aws-verify', | ||
| headers: request.headers | ||
| }; | ||
|
|
||
| // Add request method and URL as query parameters since mahi needs them for verification | ||
| var qs = require('querystring'); | ||
|
|
||
| /* Add method and URL as query parameters for mahi verification */ | ||
| var queryParams = { | ||
| method: request.method, | ||
| url: request.url | ||
| }; | ||
| requestOptions.path += '?' + qs.stringify(queryParams); | ||
|
|
||
| self.http.post(requestOptions, {}, function (err, req, res, obj) { | ||
| if (err) { | ||
| cb(err); | ||
|
|
@@ -877,6 +877,112 @@ MahiClient.prototype.verifySigV4 = function verifySigV4(request, cb) { | |
| }; | ||
|
|
||
|
|
||
| /** | ||
| * @brief Push an access key to mahi's Redis cache | ||
| * | ||
| * Writes the key directly to Redis, bypassing the UFDS replication delay. | ||
| * Best-effort: errors are logged but not propagated to the caller. | ||
| * | ||
| * @param {Object} opts | ||
| * opts.accesskeyid: {string} Key ID (required) | ||
| * opts.accesskeysecret: {string} Secret (required) | ||
| * opts.ownerUuid: {string} Owner UUID (required) | ||
| * opts.status: {string} 'Active' or 'Inactive' | ||
| * opts.scope: {string|null} Scope JSON | ||
| * @param {Function} cb - callback(err) | ||
| */ | ||
| MahiClient.prototype.cachePush = | ||
|
danmcd marked this conversation as resolved.
|
||
| function cachePush(opts, cb) { | ||
| assert.object(opts, 'opts'); | ||
| assert.string(opts.accesskeyid, 'opts.accesskeyid'); | ||
| assert.string(opts.accesskeysecret, | ||
| 'opts.accesskeysecret'); | ||
| assert.string(opts.ownerUuid, 'opts.ownerUuid'); | ||
| assert.optionalString(opts.status, 'opts.status'); | ||
| assert.optionalString(opts.scope, 'opts.scope'); | ||
| assert.func(cb, 'callback'); | ||
|
|
||
| /* | ||
| * Validate scope at the client boundary to prevent malformed scope strings | ||
| * from poisoning the Redis cache. Null/undefined scope is valid | ||
| * (unrestricted key). | ||
| */ | ||
| if (opts.scope) { | ||
| var parsed = scopeSchema.parseScope(opts.scope); | ||
| if (parsed === null) { | ||
| cb(new Error('cachePush: opts.scope is not' + | ||
| ' valid scope JSON')); | ||
| return; | ||
| } | ||
| var result = scopeSchema.validateScope(parsed); | ||
| if (!result.valid) { | ||
| cb(new Error('cachePush: invalid scope: ' + result.error)); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| var self = this; | ||
| var path = sprintf('/cache-push/%s', | ||
| opts.accesskeyid); | ||
| var body = { | ||
| accesskeysecret: opts.accesskeysecret, | ||
| ownerUuid: opts.ownerUuid, | ||
| status: opts.status || 'Active', | ||
| scope: opts.scope || null | ||
| }; | ||
|
|
||
| self.http.post(path, body, | ||
| function (err, req, res, obj) { | ||
| if (err) { | ||
| if (self.http.log) { | ||
| self.http.log.warn({ | ||
| err: err, | ||
| accesskeyid: opts.accesskeyid | ||
| }, 'cachePush: mahi call failed' + | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This and the following line can be squashed and made one constant string. |
||
| ' (non-fatal)'); | ||
| } | ||
| cb(err); | ||
| return; | ||
| } | ||
| cb(null, obj); | ||
| }); | ||
| }; | ||
|
|
||
|
|
||
| /** | ||
| * @brief Revoke an access key from mahi's Redis cache | ||
| * | ||
| * Deletes the key immediately from Redis, bypassing | ||
| * the UFDS replication delay. Best-effort: errors | ||
| * are logged but not propagated. | ||
| * | ||
| * @param {string} accesskeyid - Key ID to revoke | ||
| * @param {Function} cb - callback(err) | ||
| */ | ||
| MahiClient.prototype.scopeRevoke = | ||
| function scopeRevoke(accesskeyid, cb) { | ||
| assert.string(accesskeyid, 'accesskeyid'); | ||
| assert.func(cb, 'callback'); | ||
|
|
||
| var self = this; | ||
| var path = sprintf('/key-revoke/%s', accesskeyid); | ||
|
|
||
| self.http.post(path, {}, function (err, req, res, obj) { | ||
| if (err) { | ||
| if (self.http.log) { | ||
| self.http.log.warn({ | ||
| err: err, | ||
| accesskeyid: accesskeyid | ||
| }, 'scopeRevoke: mahi call failed' + ' (non-fatal)'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two const strings don't need to be
|
||
| } | ||
| cb(err); | ||
| return; | ||
| } | ||
| cb(null, obj); | ||
| }); | ||
| }; | ||
|
|
||
|
|
||
| module.exports = { | ||
| MahiClient: MahiClient, | ||
| createClient: function (opts) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does cachePush need to be put here too? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one (cachePush) I need an answer to, and then I'm likely good.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, as it's defined as a prototype it will be available when a new MahiClient is instanciated. |
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.