Skip to content
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

Implement SSO using cookie and .config file. #32

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .config
Original file line number Diff line number Diff line change
@@ -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
}
53 changes: 13 additions & 40 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <bookid>','the book id of the SafariBooksOnline ePub to be generated')
.option('-u, --username <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>','password of the SafariBooksOnline user')
.option('-o, --output <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");
Expand Down
101 changes: 30 additions & 71 deletions lib/safari/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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"],
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down