From b6ad95641e527480e8bcca362a92cfd246b53253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20Carrasco?= Date: Sun, 17 Jun 2018 16:16:39 -0600 Subject: [PATCH] Implement SSO using cookie and .config file. --- .config | 4 ++ lib/cli/index.js | 53 ++++++----------------- lib/safari/index.js | 101 +++++++++++++------------------------------- package.json | 3 +- 4 files changed, 49 insertions(+), 112 deletions(-) create mode 100644 .config diff --git a/.config b/.config new file mode 100644 index 0000000..69ca78a --- /dev/null +++ b/.config @@ -0,0 +1,4 @@ +{ + "cookie":"corp_sessionid=hashvalue; BrowserCookie=hashvalue; original_referer=direct; _ga=GA1.2.1043727385.1529170946; _gid=GA1.2.88305855.1529170946; timezoneoffset=21600; _vwo_uuid_v2=hashvalue; kampyle_userid=hashvalue; cd_user_id=hashvalue; sessionid=hashvalue; csrfsafari=hashvalue; salesforce_id=hashvalue; logged_in=y; kampyleUserSession=hashvalue; kampyleUserSessionsCount=3; kampyleUserPercentile=value; recently-viewed=%value; kampyleSessionPageCounter=18; _gali=sbo-rt-content; _gat=1", + "bookid":9781491925607 +} \ No newline at end of file diff --git a/lib/cli/index.js b/lib/cli/index.js index 9469740..7249e3e 100644 --- a/lib/cli/index.js +++ b/lib/cli/index.js @@ -2,60 +2,33 @@ // sets up the Command Line Interface 'use strict'; -const program = require('commander'); const logger = require('../logger'); const Safari = require('../safari'); const EbookWriter = require('../ebook-writer'); const debug = require('debug')('cli'); const fs = require('fs-promise'); -// ## Commander Initialization -// specifying CLI options and version -program - .version("1.0.0") - .option('-b, --bookid ','the book id of the SafariBooksOnline ePub to be generated') - .option('-u, --username ','username of the SafariBooksOnline user - must have a **paid/trial membership**, otherwise will not be able to access the books') - .option('-p, --password ','password of the SafariBooksOnline user') - .option('-o, --output ','output path the epub file should be saved to') - .parse(process.argv); -var username, password; - -// ## see whether .credentials already exists - if (fs.existsSync(__dirname + '/.credentials')) { - var credentials = JSON.parse(fs.readFileSync(__dirname + '/.credentials', 'utf-8')); - username = credentials.username; - password = credentials.password; - debug("there is an existing user cached with username: " + username); +// ## see whether .cookie already exists +if (!fs.existsSync(__dirname + '/../../.config')) { + logger.log("Please create the configuration file"); + return; +} else { + var config = JSON.parse(fs.readFileSync(__dirname + '/../../.config', 'utf-8')); } -// ## overwrite username and password if specified -if(program.username) username = program.username; -if(program.password) password = program.password; - -// ## Validate Input -if(!program.bookid) return console.log("error: option '-b' missing. please consider '--help' for more information."); -if(!username) return console.log("error: option '-u' missing. please consider '--help' for more information."); -if(!password) return console.log("error: option '-p' missing. please consider '--help' for more information."); -if(!program.output) return console.log("error: option '-o' missing. please consider '--help' for more information."); - -// ## Starting CLI -logger.log(`starting application...`); +const { bookid, cookie } = config; -// ## writing credentials to file -let json = JSON.stringify({ "username": username, "password": password }); -fs.writeFile(__dirname + '/.credentials', json).then( () => { - debug(`the username and password were successfully cached`); -}).catch( (err) => { - debug(`an error occurred trying to write the username and pass to the cache file`); -}); +logger.log(`cookie ${cookie}`); +logger.log(`bookid ${bookid}`); // ## Authorize User -var safariClient = new Safari(); -safariClient.fetchBookById(program.bookid, username, password).then( (bookJSON) => { +var safariClient = new Safari(cookie); +safariClient.fetchBookById(bookid).then( (bookJSON) => { // console.log(bookJSON); var ebook = new EbookWriter(bookJSON); - return ebook.save(program.output); + logger.log(__dirname + '/../../books/' + bookJSON.title + '.epub'); + return ebook.save(__dirname + '/../../books/' + bookJSON.title + '.epub'); }).then( () => { // ## finished saving debug("the epub was successfully saved"); diff --git a/lib/safari/index.js b/lib/safari/index.js index 0fa3460..6203f6d 100644 --- a/lib/safari/index.js +++ b/lib/safari/index.js @@ -13,13 +13,13 @@ const debug = require('debug')('safari'), * * @return {Object} safari - returns a safari object */ -const Safari = function Safari() { - +const Safari = function Safari(cookie) { // ## prepare base settings this.baseUrl = "https://www.safaribooksonline.com"; this.clientSecret = "f52b3e30b68c1820adb08609c799cb6da1c29975"; this.clientId = "446a8a270214734f42a7"; this.books = {}; + this.cookie = cookie; } /** @@ -56,52 +56,6 @@ Safari.prototype.fetchStylesheet = function fetchStylesheet(id) { Promise.resolve(stylesheetUrl); } -/** -* ## Authorize User -* authorizes an user with the given credentials and sets the authorization key -* -* @param {String} username - the username of the safaribooksonline account -* @param {String} password - the password of the safaribooksonline account -* -* @return {String} accessToken - returns the access token -*/ -Safari.prototype.authorizeUser = function authorizeUser(username, password) { - debug(`authorizeUser called with user ${username} and password ${password}`); - if (!username || !password) return Promise.reject("username or password was not specified"); - // ## prepare options for oauth request - let options = { - method: 'POST', - uri: `${this.baseUrl}/oauth2/access_token/`, - form: { - "client_id" : this.clientId, - "client_secret" : this.clientSecret, - "grant_type" : "password", - "username" : username, - "password" : password - }, - json: true - }; - // ## make API call in order to retrieve Bearer Authorization Token - return request(options) - .then( (body) => { - // ### the token was successfully generated - let accessToken = body["access_token"]; - if (!accessToken) { - debug('the access_token is not present in the server response, even though it returned an 200 OK'); - return Promise.reject("The access_token is not present in the server response. Please contact the developer to fix this problem.") - } - this.accessToken = accessToken; - debug(`the access_token is: ${accessToken}`); - return Promise.resolve(accessToken); - }) - .catch( (err) => { - // ### an error occured - debug(`an error occured while trying to fetch authorization token (error: ${err})`); - return Promise.reject(err); - }); -} - - /** * ## Fetch Resource * fetches a resource with the given url and automatically adds the required authorization token @@ -113,12 +67,12 @@ Safari.prototype.authorizeUser = function authorizeUser(username, password) { */ Safari.prototype.fetchResource = function fetchResource(url, options) { debug(`fetchResource called with URL: ${url}`); - if(!url || !this.accessToken) return Promise.reject("url was not specified or user has not been authorized yet"); + if(!url) return Promise.reject("url was not specified or user has not been authorized yet"); // ## prepare options for resource request var uri = `${this.baseUrl}/${url}`; var json = true; var headers = { - "authorization": `Bearer ${this.accessToken}` + "cookie": this.cookie }; if(options && options.json == false) json = false; if(options && options.uri) uri = options.uri; @@ -239,21 +193,34 @@ Safari.prototype.getBookById = function getBookById(id) { if(!id) return Promise.reject("id was not specified"); if(!this.books[id]) return Promise.reject("the book you requested was not fetched yet"); let book = this.books[id]; - if(!book.meta || !book.chapters) return Promise.reject("the book you requested is missing some required information"); + let { + meta: { + title, + identifier, + language, + authors, + cover, + description, + publishers + }, + stylesheet, + chapters + } = book; + if(!chapters) return Promise.reject("the book you requested is missing some required information"); let jsonBook = { - "title": book.meta.title, - "uuid": book.meta.identifier, - "language": book.meta.language, - "author": book.meta.authors.map( (json) => { + "title": title, + "uuid": identifier, + "language": language, + "author": authors.map( (json) => { return json.name; }), - "cover": book.meta.cover, - "description": book.meta.description, - "publisher": book.meta.publishers.map( (json) => { + "cover": cover, + "description": description, + "publisher": publishers.map( (json) => { return json.name; }), - "stylesheet": book.stylesheet, - "chapters": book.chapters.map( (chapter) => { + "stylesheet": stylesheet, + "chapters": chapters.map( (chapter) => { return { "fileName": chapter.filename, "assetBase": chapter["asset_base_url"], @@ -273,22 +240,14 @@ Safari.prototype.getBookById = function getBookById(id) { * returns the book content * * @param {String} id - the id of the specified book -* @param {String} username - the username of the safaribook user -* @param {String} password - the password of the safaribook user * * @return {Object} body - returns the book json */ -Safari.prototype.fetchBookById = function fetchBookById(id, username, password) { +Safari.prototype.fetchBookById = function fetchBookById(id) { debug(`fetchBookById called with id: ${id}`); if(!id) return Promise.reject("id was not specified"); - if(!username || !password) return Promise.reject("username or password was not specified"); - // ## validation successful - // ## fetch required content - return this.authorizeUser(username, password).then( (accessToken) => { - // ### user authorized, fetch meta - logger.log(`the user "${username}" was successfully authorized...`); - return this.fetchMeta(id); - }).then( (meta) => { + return this.fetchMeta(id) + .then( (meta) => { // ### meta fetched; fetch flat-toc logger.log(`downloaded meta files...`); return this.fetchTOC(id); diff --git a/package.json b/package.json index 9a376e2..5e5877d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "a SafariBooksOnline-Downloader and .epub creator", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "safaribooks": "node ./index.js" }, "bin": { "safaribooks-downloader": "./index.js"