Skip to content

Commit

Permalink
Merge pull request #78 from pliancy/fix/auth
Browse files Browse the repository at this point in the history
Fix: Auth
  • Loading branch information
santese authored Jul 20, 2021
2 parents c5843e8 + d2ac73a commit d048e5e
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 78 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
}
},
"dependencies": {
"axios": "0.21.1"
"axios": "0.21.1",
"cheerio": "^1.0.0-rc.10"
},
"description": "Library for managing things against the undocumented egnyte resellers API.",
"devDependencies": {
Expand Down
110 changes: 50 additions & 60 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,63 @@
// import Egnyte from './index'
import Egnyte from './'

// const egnyte = new Egnyte({
// username: 'a',
// password: 'b'
// })
describe('Egnyte', () => {
let egnyte: Egnyte

import Egnyte from './index'
beforeEach(() => {
egnyte = new Egnyte({
username: 'a',
password: 'b',
})
})

describe('http config', () => {
it('defaults to a 20000ms timeout given timeoutMs is undefined', async () => {
const ern = new Egnyte({
username: 'user',
password: 'pass',
})

describe('http config', () => {
it('defaults to a 20000ms timeout given timeoutMs is undefined', async () => {
const ern = new Egnyte({
username: 'user',
password: 'pass',
expect(ern['httpConfig'].timeout).toBe(20000)
})

expect(ern['httpConfig'].timeout).toBe(20000)
})
it('defaults to a 20000ms timeout given timeoutMs is not parsable as an integer', async () => {
const ern = new Egnyte({
username: 'user',
password: 'pass',
timeoutMs: 'NotANumber' as any,
})

it('defaults to a 20000ms timeout given timeoutMs is not parsable as an integer', async () => {
const ern = new Egnyte({
username: 'user',
password: 'pass',
timeoutMs: 'NotANumber' as any,
expect(ern['httpConfig'].timeout).toBe(20000)
})

expect(ern['httpConfig'].timeout).toBe(20000)
})
it('sets timeout given timeoutMs is parsable as an integer', async () => {
const ern = new Egnyte({
username: 'user',
password: 'pass',
timeoutMs: '30000,' as any,
})

it('sets timeout given timeoutMs is parsable as an integer', async () => {
const ern = new Egnyte({
username: 'user',
password: 'pass',
timeoutMs: '30000,' as any,
expect(ern['httpConfig'].timeout).toBe(30000)
})
})

expect(ern['httpConfig'].timeout).toBe(30000)
describe('helpers', () => {
it('gets CSRF tokens', async () => {
const csrfMiddlewareToken = 'fec9a59a86510210de334ca4e251ed3d'
const csrfToken = '12345'
const data = `<input type='hidden' id='csrfmiddlewaretoken' name='csrfmiddlewaretoken' value=${csrfMiddlewareToken} />`
jest.spyOn(egnyte, '_egnyteRequest' as never).mockImplementation(
() =>
({
data,
headers: { 'set-cookie': [`csrftoken=${csrfToken}; expires=Some Date;`] },
} as never),
)
let result = await egnyte['_getCsrfTokens']()
return expect(result).toEqual({
csrfMiddlewareToken,
csrfToken,
})
})
})
})

// describe('Helpers', () => {
// beforeEach(() => {
// fetch.resetMocks()
// })

// xit('Gets CSRF token', async () => {
// fetch.mockResponse(`<div id="leftcolumn" class="leftcolumn-center login-area">
// <div class="modulehead">Login</div>
// <div id="userlist" class="userlist-full editlist" style="background-color: #f5f5ef;">

// <form action="" method="post" id="login-form"><div style='display:none;'><input type='hidden' id='csrfmiddlewaretoken' name='csrfmiddlewaretoken' value='fec9a59a86510210de334ca4e251ed3d' /></div>
// <p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="100" /></p>
// <p><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></p>

// <input type="hidden" name="this_is_the_login_form" value="1" />
// <input type="submit" class="button" value="Log in" />
// </form>
// <ul>
// <li><a href="/accounts/recover_account/">Can't access your account?</a></li>
// </ul>
// </div>
// </div>
// `, { headers: { 'location': [ 'test' ] } })
// let result = await egnyte._getCsrfToken()
// return expect(result).toEqual('fec9a59a86510210de334ca4e251ed3d')
// })

// xit('Gets resellerId from header', async () => {
// fetch.mockResponse(`who cares`, { headers: { location: 'https://resellers.egnyte.com/msp/plan/12345/' }, status: 302 })
// let result = await egnyte._getResellerId('mycookie=cool')
// return expect(result).toEqual('12345')
// })
// })
36 changes: 23 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import qs from 'querystring'
import axios, { AxiosRequestConfig } from 'axios'
import cheerio from 'cheerio'

interface EgnyteCustomer {
customerEgnyteId: string
Expand Down Expand Up @@ -79,14 +80,19 @@ class Egnyte {
* Gets a csrf token and returns it
* @returns the csrf token
*/
private async _getCsrfToken(): Promise<string> {
const { data: query } = await this._egnyteRequest(
'/accounts/login/?next=/customer/browse/',
this.httpConfig,
)
const csrfRegexp = query.match(/id='csrfmiddlewaretoken'.*value='([a-zA-Z0-9]+)'.*\n/)
if (!csrfRegexp) throw new Error('unable to find CSRF token in egnyte resellers login page')
return csrfRegexp[1]
private async _getCsrfTokens() {
const res = await this._egnyteRequest('accounts/login/', this.httpConfig)
// Use Cheerio to parse webpage for csrfmiddlewaretoken
const html = cheerio.load(res.data)
const csrfMiddlewareToken = html('[name=csrfmiddlewaretoken]').val()

// Regex cookies to get csrfToken
const csrfToken = res.headers['set-cookie']
.find((e: string) => e.includes('csrftoken'))
.match(/csrftoken=(.*); expires/)[1]
if (!csrfMiddlewareToken || !csrfToken)
throw new Error('unable to find CSRF token in egnyte resellers login page')
return { csrfMiddlewareToken, csrfToken }
}

/**
Expand Down Expand Up @@ -134,17 +140,21 @@ class Egnyte {
/**
* authenticate to egnyte api. gets auth cookie
* @param username the username for auth
* @param password the passworf for auth
* @param password the password for auth
* @returns the auth cookie string
*/
private async _authenticate(): Promise<string> {
const csrfToken = await this._getCsrfToken()
const auth = await this._egnyteRequest('/accounts/login/?next=/customer/browse/', {
private async _authenticate(): Promise<any> {
const { csrfMiddlewareToken, csrfToken } = await this._getCsrfTokens()
const auth = await this._egnyteRequest('/accounts/login/', {
method: 'post',
data: `csrfmiddlewaretoken=${csrfToken}&username=${qs.escape(
data: `csrfmiddlewaretoken=${csrfMiddlewareToken}&username=${qs.escape(
this._config.username,
)}&password=${qs.escape(this._config.password)}&this_is_the_login_form=1`,
maxRedirects: 0,
headers: {
Referer: 'https://resellers.egnyte.com/accounts/login/',
Cookie: `csrftoken=${csrfToken}`,
},
validateStatus: (status) => status >= 200 && status <= 303,
})
if (auth.status === 302) {
Expand Down
Loading

0 comments on commit d048e5e

Please sign in to comment.