Last active
August 17, 2018 05:43
-
-
Save loopmode/9f72d22fcadc58c16addac52aaef4fac to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as spauth from 'node-sp-auth'; | |
import getBaseUrl from '../utils/getBaseUrl'; | |
export const AuthMethod = { | |
NONE: 'NONE', | |
NTLM: 'NTLM', | |
BASIC: 'BASIC' | |
}; | |
export function getAuthMethod(authenticate) { | |
if (!authenticate) { | |
return AuthMethod.NONE; | |
} | |
if (authenticate.match(/NTLM/i)) { | |
return AuthMethod.NTLM; | |
} | |
if (authenticate.match(/Basic /i)) { | |
return AuthMethod.BASIC; | |
} | |
console.warn(`Unsupported authentication method: ${authenticate}`); | |
return undefined; | |
} | |
export function getAuthOptions(data) { | |
switch (getAuthMethod(data.authenticate)) { | |
case AuthMethod.BASIC: | |
return getAuthOptionsBasic(data); | |
case AuthMethod.NTLM: | |
return getAuthOptionsNTLM(data); | |
default: | |
return {}; | |
} | |
} | |
export function getAuthOptionsBasic({ username, password }) { | |
return { | |
headers: { | |
Authorization: 'Basic ' + new Buffer(username + ':' + password).toString('base64') | |
} | |
}; | |
} | |
export async function getAuthOptionsNTLM({ url, username, password, domain }) { | |
if (username.indexOf('\\') !== -1) { | |
const parts = username.split('\\'); | |
domain = parts[0]; | |
username = parts[1]; | |
} | |
const data = await spauth.getAuth(getBaseUrl(url), { | |
username, | |
password, | |
domain | |
}); | |
return { | |
...data.options, | |
headers: data.headers | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs-extra'; | |
import path from 'path'; | |
import crypto from 'crypto'; | |
import * as request from 'request'; | |
import config from '../config'; | |
import ExternalAuthError from '../errors/ExternalAuthError'; | |
import { getAuthOptions } from './Auth'; | |
const tempDir = path.resolve(config.app.storage.attachments, '.temp'); | |
/** | |
* Downloads a file from a URL to a (temp) location and saves it with a random filename. | |
* Returns a promise that is resolved with the full path to the downloaded file. | |
* Handles Basic and NTLM auth when given `user`, `password` and `authenticate` | |
* | |
* @param {String} url - the remote file to be downloaded | |
* @param {Object} [options] - An options object | |
* @param {String} [options.dir = tempDir] - Local path to a destination folder for the downloaded file. | |
* @param {String} [options.authenticate] - Type of authentication required by remote file. of `BASIC` or `NTLM` | |
* @param {String} [options.domain] - Domain for remote authentication. May be part of username as well. | |
* @param {String} [options.username] - Username for remote authentication. May contain a domain name (e.g. `domain\username`) | |
* @param {String} [options.password] - Password for remote authentication | |
*/ | |
export default async function downloadToTemp(url, { dir = tempDir, authenticate, domain, username, password } = {}) { | |
const authOptions = await getAuthOptions({ url, username, password, domain, authenticate }); | |
const requestOptions = { url, ...authOptions }; | |
return new Promise((resolve, reject) => { | |
// start off with a paused stream - we need to check response headers first, then resume | |
// that's because the on('error') handler is not invoked on all errors - e.g. http error responses >=400 slip through | |
// see https://stackoverflow.com/questions/7222982/node-request-how-to-determine-if-an-error-occurred-during-the-request/26163128#26163128 | |
const req = request.get(requestOptions); | |
req.pause(); | |
// however, it is invoked in some error cases | |
req.on('error', function(error) { | |
reject(error); | |
}); | |
req.on('response', async response => { | |
if (response.statusCode === 401) { | |
// Authentication required | |
reject(new ExternalAuthError(response)); | |
} else if (response.statusCode >= 400) { | |
// Some other error | |
reject(new Error(response.statusCode)); | |
} else { | |
// Download is available | |
try { | |
await fs.ensureDir(dir); | |
const id = crypto.randomBytes(20).toString('hex'); | |
const filepath = path.resolve(dir, id); | |
req.pipe(fs.createWriteStream(filepath)); | |
req.on('end', () => resolve(filepath)); | |
req.resume(); | |
} catch (error) { | |
reject(error); | |
} | |
} | |
}); | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Downloads a file from a URL and puts it in the user uploads folder. | |
* Creates a new Attachment node for that file in orientdb and connects it to the user node. | |
* Supports NTLM an basic auth (e.g. sharepoint, htaccess) | |
* Returns a promise. | |
* | |
* @param {String} url - The URL of the file | |
* @param {User|String} user - A user object or user ID | |
* @param {Object} [options] - An options object | |
* @param {String} [options.filename] - An optional name for the saved file. Defaults to the last segment of the URL | |
* @return {Object} - A `{ filename, filepath, hash }` object | |
*/ | |
async function downloadForUser(url, userId, options = {}) { | |
const tempfilePath = await downloadToTemp(url, { | |
username: options.username, | |
password: options.password, | |
authenticate: options.authenticate | |
}); | |
const hash = await getFileHash(tempfilePath); | |
const filename = options.filename || getFileName(url); | |
const dirpath = path.resolve(config.app.storage.attachments, userId, hash); | |
const filepath = path.resolve(dirpath, filename); | |
await fs.ensureDir(dirpath); | |
await fs.move(tempfilePath, filepath, { overwrite: true }); | |
const fileInfo = { filename, filepath, hash }; | |
const attachment = await this.getUserAttachment(fileInfo, userId); | |
if (attachment.vertex) { | |
const { vertex, edge } = attachment; | |
return { fileInfo, vertex, edge }; | |
} else { | |
const { vertex, edge } = await Attachments.createUserAttachment(fileInfo, userId); | |
return { fileInfo, vertex, edge }; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment