UNPKG

5.61 kBJavaScriptView Raw
1// Copyright © 2015, 2018 IBM Corp. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14'use strict';
15
16// reconfigure deals with the various ways the credentials can be passed in
17// and returns an full URL
18// e.g. { account:"myaccount", password: "mypassword"}
19// or { key: "mykey", password: "mykey", account:"myaccount"}
20// or { key: "mykey", password: "mykey", account:"myaccount"}
21// or { account:"myaccount.cloudant.com", password: "mykey"}
22// or { account: "myaccount"}
23// or { url: "https://mykey:mypassword@myaccount.cloudant.com"}
24// or { instanceName: "mycloudantservice", vcapServices: JSON.parse(process.env.VCAP_SERVICES)}
25
26var url = require('url');
27
28module.exports = function(config) {
29 config = JSON.parse(JSON.stringify(config)); // clone
30 var outUrl;
31 var outCreds = {};
32 var options;
33 var username;
34 var password;
35 // if a full URL is passed in
36 if (config.url) {
37 // parse the URL
38 var parsed = null;
39 try {
40 parsed = url.parse(config.url);
41 } catch (e) {
42 parsed = null;
43 }
44 if (!config.url || !parsed || !parsed.hostname || !parsed.protocol || !parsed.slashes) {
45 outCreds.outUrl = null;
46 return outCreds;
47 }
48
49 // enforce HTTPS for *cloudant.com domains
50 if (parsed.hostname.match(/cloudant\.com$/) && parsed.protocol === 'http:') {
51 console.warn('WARNING: You are sending your password as plaintext over the HTTP; switching to HTTPS');
52
53 // force HTTPS
54 parsed.protocol = 'https:';
55
56 // remove port number and path
57 parsed.host = parsed.host.replace(/:[0-9]*$/, '');
58 delete parsed.port;
59 delete parsed.pathname;
60 delete parsed.path;
61
62 // reconstruct the URL
63 config.url = url.format(parsed);
64 } else {
65 options = getOptions(config);
66 username = options.username;
67 password = options.password;
68 if (username && password) {
69 config.url = parsed.protocol + '//' + encodeURIComponent(username) + ':' +
70 encodeURIComponent(password) + '@' + parsed.host;
71 }
72 }
73 outUrl = config.url;
74 } else if (config.vcapServices) {
75 var cloudantServices;
76 if (typeof config.vcapServiceName !== 'undefined') {
77 cloudantServices = config.vcapServices[config.vcapServiceName];
78 } else {
79 cloudantServices = config.vcapServices.cloudantNoSQLDB;
80 }
81
82 if (!cloudantServices || cloudantServices.length === 0) {
83 throw new Error('Missing Cloudant service in vcapServices');
84 }
85
86 if (typeof config.vcapInstanceName !== 'undefined') {
87 config.instanceName = config.vcapInstanceName; // alias
88 }
89
90 for (var i = 0; i < cloudantServices.length; i++) {
91 if (typeof config.instanceName === 'undefined' || cloudantServices[i].name === config.instanceName) {
92 var credentials = cloudantServices[i].credentials;
93 if (credentials && credentials.host) {
94 outUrl = 'https://' + encodeURIComponent(credentials.host);
95 if (credentials.apikey) {
96 outCreds.iamApiKey = credentials.apikey;
97 } else if (credentials.username && credentials.password) {
98 outUrl = 'https://' + encodeURIComponent(credentials.username) + ':' +
99 encodeURIComponent(credentials.password) + '@' + encodeURIComponent(credentials.host);
100 }
101 break;
102 } else {
103 throw new Error('Invalid Cloudant service in vcapServices');
104 }
105 }
106 }
107
108 if (!outUrl) {
109 throw new Error('Missing Cloudant service in vcapServices');
110 }
111 } else {
112 // An account can be just the username, or the full cloudant URL.
113 var match = config.account &&
114 config.account.match &&
115 config.account.match(/([^.]+)\.cloudant\.com/);
116 if (match) { config.account = match[1]; }
117 options = getOptions(config);
118 username = options.username;
119 password = options.password;
120
121 // Configure for Cloudant, either authenticated or anonymous.
122 if (config.account && password) {
123 config.url = 'https://' + encodeURIComponent(username) + ':' +
124 encodeURIComponent(password) + '@' +
125 encodeURIComponent(config.account) + '.cloudant.com';
126 } else if (config.account) {
127 config.url = 'https://' + encodeURIComponent(config.account) +
128 '.cloudant.com';
129 }
130
131 outUrl = config.url;
132 }
133
134 // We trim out the trailing `/` because when the URL tracks down to `nano` we have to
135 // worry that the trailing `/` doubles up depending on how URLs are built, this creates
136 // "Database does not exist." errors.
137 // Issue: cloudant/nodejs-cloudant#129
138 if (outUrl && outUrl.slice(-1) === '/') {
139 outUrl = outUrl.slice(0, -1);
140 }
141
142 outCreds.outUrl = (outUrl || null);
143 return outCreds;
144};
145
146module.exports.getOptions = getOptions;
147function getOptions(config) {
148 // The username is the account ("foo" for "foo.cloudant.com")
149 // or the third-party API key.
150 var result = {password: config.password, username: config.key || config.username || config.account};
151 return result;
152}