Reputation: 219
I have created a Cloudant service and credentials (1) along with a database and a couple of documents. I want to access that database from an IBM Cloud Function so created a function to do that. I tested the function by adding the copied and pasted credentials from (1) into the "Invoke with credentials" box (along with the and the db name and a valid docid). The function seems to take all that info correctly but I get a 401 error... any help would be massively appreciated!
{
"error": {
"description": "couch returned 401",
"errid": "non_200",
"error": "unauthorized",
"headers": {
"cache-control": "must-revalidate",
"content-type": "application/json",
"date": "Sat, 30 May 2020 16:37:25 GMT",
"statusCode": 401,
"strict-transport-security": "max-age=31536000",
"uri": "xxxxxxx",
"via": "1.1 lb1.bm-cc-eu-gb-04 (Glum/1.89.6)",
"www-authenticate": "Basic realm=\"Cloudant Private Database\"",
"x-cloudant-action": "cloudantnosqldb.any-document.read",
"x-cloudant-backend": "bm-cc-eu-gb-04",
"x-cloudant-request-class": "lookup",
"x-content-type-options": "nosniff",
"x-couch-request-id": "03e7fe91bb"
},
"message": "_reader access is required for this request",
"name": "Error",
"reason": "_reader access is required for this request",
"request": {
"headers": {
"accept": "application/json",
"content-type": "application/json"
},
"method": "GET",
"uri": "xxxxx"
},
"scope": "couch",
"stack": "Error: _reader access is required for this request\n at Object.clientCallback (/node_modules/@cloudant/cloudant/node_modules/nano/lib/nano.js:151:15)\n at Request._callback (/node_modules/@cloudant/cloudant/lib/clientutils.js:162:11)\n at Request.self.callback (/node_modules/request/request.js:185:22)\n at Request.emit (events.js:198:13)\n at Request.self._source.emit (/node_modules/@cloudant/cloudant/lib/eventrelay.js:78:21)\n at Request.<anonymous> (/node_modules/request/request.js:1161:10)\n at Request.emit (events.js:198:13)\n at Request.self._source.emit (/node_modules/@cloudant/cloudant/lib/eventrelay.js:78:21)\n at IncomingMessage.<anonymous> (/node_modules/request/request.js:1083:12)",
"statusCode": 401
}
}
The function code is as follows:
/**
* Read a document in Cloudant database:
* https://docs.cloudant.com/document.html#read
**/
function main(message) {
var cloudantOrError = getCloudantAccount(message);
if (typeof cloudantOrError !== 'object') {
return Promise.reject(cloudantOrError);
}
var cloudant = cloudantOrError;
var dbName = message.dbname;
var docId = message.docid || message.id;
var params = {};
if (!dbName) {
return Promise.reject('dbname is required.');
}
if (!docId) {
return Promise.reject('docid is required.');
}
var cloudantDb = cloudant.use(dbName);
if (typeof message.params === 'object') {
params = message.params;
} else if (typeof message.params === 'string') {
try {
params = JSON.parse(message.params);
} catch (e) {
return Promise.reject('params field cannot be parsed. Ensure it is valid JSON.');
}
}
return readDocument(cloudantDb, docId, params);
}
function readDocument(cloudantDb, docId, params) {
return new Promise(function (resolve, reject) {
cloudantDb.get(docId, params, function (error, response) {
if (!error) {
resolve(response);
} else {
console.error('error', error);
reject(error);
}
});
});
}
function getCloudantAccount(params) {
var Cloudant = require('@cloudant/cloudant');
var cloudant;
if (!params.iamApiKey && params.url) {
cloudant = Cloudant(params.url);
} else {
checkForBXCreds(params);
if (!params.host) {
return 'Cloudant account host is required.';
}
if (!params.iamApiKey) {
if (!params.username || !params.password) {
return 'You must specify parameter/s of iamApiKey or username/password';
}
}
var protocol = params.protocol || 'https';
if (params.iamApiKey) {
var dbURL = `${protocol}://${params.host}`;
if (params.port) {
dbURL += ':' + params.port;
}
cloudant = new Cloudant({
url: dbURL,
plugins: {iamauth: {iamApiKey: params.iamApiKey, iamTokenUrl: params.iamUrl}}
});
} else {
var url = `${protocol}://${params.username}:${params.password}@${params.host}`;
if (params.port) {
url += ':' + params.port;
}
cloudant = Cloudant(url);
}
}
return cloudant;
}
function checkForBXCreds(params) {
if (params.__bx_creds && (params.__bx_creds.cloudantnosqldb || params.__bx_creds.cloudantNoSQLDB)) {
var cloudantCreds = params.__bx_creds.cloudantnosqldb || params.__bx_creds.cloudantNoSQLDB;
if (!params.host) {
params.host = cloudantCreds.host || (cloudantCreds.username + '.cloudant.com');
}
if (!params.iamApiKey && !cloudantCreds.apikey) {
if (!params.username) {
params.username = cloudantCreds.username;
}
if (!params.password) {
params.password = cloudantCreds.password;
}
} else if (!params.iamApiKey) {
params.iamApiKey = cloudantCreds.apikey;
}
}
}
Upvotes: 0
Views: 674
Reputation: 219
Basically, copying and pasting those credentials led to it not working. Not sure why. To get a test invocation working I added url, docid, dbname, host, url and iamApiKey values to the parameters section of the function. That worked.
Upvotes: 0