Skip to content

Commit

Permalink
Merge pull request #800 from steve-winter/#1-Bitbucket
Browse files Browse the repository at this point in the history
Resolves issue #1 - Enables Bitbucket Auth and Repo
  • Loading branch information
steve-winter authored Nov 25, 2023
2 parents e8eb7bc + d2f4dd3 commit 70fad8e
Show file tree
Hide file tree
Showing 40 changed files with 1,135 additions and 399 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@

# Express server
!td.server
!td.server/src/**/*.js
!td.server/test/**/*.js
!td.server/**/*.js
!td.server/**/**/*.js
!td.server/.babelrc
!td.server/README.md
!td.server/package.json
Expand All @@ -61,7 +64,6 @@
!td.vue/src/assets/*.jpg
!td.vue/src/assets/*.svg
!td.vue/src/components/
!td.vue/src/components/
!td.vue/src/components/printed-report/
!td.vue/src/components/report/
!td.vue/src/desktop/
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Mike Goodwin's [original](https://github.com/mike-goodwin/owasp-threat-dragon)
which has the issues and pull requests from October 2015 up to June 2020.

Threat Dragon is [primarily a web application](https://github.com/OWASP/threat-dragon/releases),
with threat model files stored in GitHub. Over time other storage methods will become available.
with threat model files stored in GitHub or Bitbucket. Over time other storage methods will become available.

There are [desktop versions](https://github.com/OWASP/threat-dragon/releases) of Threat Dragon
which store the threat model files on the local filesystem rather than in a repository.
Expand Down Expand Up @@ -83,8 +83,8 @@ The web application variant of Threat Dragon requires some environment variables
follow [the documentation](https://owasp.org/www-project-threat-dragon/docs-2/install-environment/)
on how to set these variables.

The Threat Dragon web application uses GitHub to store threat models,
so you need to go to your GitHub account and register it as a GitHub application.
The Threat Dragon web application uses GitHub or Bitbucket to store threat models,
so you need to go to your GitHub or Bitbucket account and register it as a GitHub application.
There is a [step by step guide](https://owasp.org/www-project-threat-dragon/docs-2/install-environment/)
on how to do this. Github Enterprise is also supported.

Expand Down
7 changes: 7 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ SERVER_API_PROTOCOL=http
ENCRYPTION_KEYS='[{"isPrimary": true, "id": 0, "value": "11223344556677889900aabbccddeeff"}]'
ENCRYPTION_JWT_SIGNING_KEY=asdfasdfasdf
ENCRYPTION_JWT_REFRESH_SIGNING_KEY=fljasdlfkjadf



BITBUCKET_WORKSPACE=workspace_name
BITBUCKET_CLIENT_ID=01234567890123456789
BITBUCKET_CLIENT_SECRET=0123456789abcdef0123456789abcdef0123456
BITBUCKET_SCOPE=repository:write
77 changes: 77 additions & 0 deletions td.server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion td.server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"resolutions": {},
"dependencies": {
"@babel/runtime": "^7.21.0",
"bitbucket": "^2.11.0",
"axios": "^1.6.0",
"dotenv": "^16.0.3",
"express": "^4.18.2",
Expand Down Expand Up @@ -69,7 +70,6 @@
"sinon-chai": "^3.7.0",
"supertest": "^4.0.2"
},

"overrides": {
"@achrinza/node-ipc": ">=10.1.10",
"@babel/traverse": ">=7.23.2",
Expand Down
3 changes: 3 additions & 0 deletions td.server/src/config/env.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import BitbucketEnv from '../env/Bitbucket.js';
import EncryptionEnv from '../env/Encryption.js';
import env from '../env/Env.js';
import GithubEnv from '../env/Github.js';
import ThreatDragonEnv from '../env/ThreatDragon.js';

const tryLoadDotEnv = () => {
const github = new GithubEnv();
const bitbucket = new BitbucketEnv();
const encryption = new EncryptionEnv();
const threatDragon = new ThreatDragonEnv();
env.get().addProvider(github);
env.get().addProvider(encryption);
env.get().addProvider(bitbucket);
env.get().addProvider(threatDragon);
env.get().hydrate();
};
Expand Down
54 changes: 47 additions & 7 deletions td.server/src/controllers/threatmodelcontroller.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import env from '../env/Env.js';
import loggerHelper from '../helpers/logger.helper.js';
import repository from '../repositories/threatmodelrepository.js';
import repositories from "../repositories";
import responseWrapper from './responseWrapper.js';
import { serverError } from './errors.js';

const logger = loggerHelper.get('controllers/threatmodelcontroller.js');

const repos = (req, res) => responseWrapper.sendResponseAsync(async () => {
const repository = repositories.get();

const page = req.query.page || 1;
let reposResp;
let repos;
Expand All @@ -21,15 +23,23 @@ const repos = (req, res) => responseWrapper.sendResponseAsync(async () => {
repos = reposResp[0];
}
const headers = reposResp[1];
const pageLinks = reposResp[2];
logger.debug('API repos request: ' + req);

const pagination = getPagination(headers, pageLinks, page);

return {
repos: repos.map((x) => x.full_name),
pagination: getPagination(headers, page)
pagination: pagination
};
}, req, res, logger);



const branches = (req, res) => responseWrapper.sendResponseAsync(async () => {

const repository = repositories.get();

const repoInfo = {
organisation: req.params.organisation,
repo: req.params.repo,
Expand All @@ -40,15 +50,21 @@ const branches = (req, res) => responseWrapper.sendResponseAsync(async () => {
const branchesResp = await repository.branchesAsync(repoInfo, req.provider.access_token);
const branches = branchesResp[0];
const headers = branchesResp[1];
const pageLinks = branchesResp[2];

const branchNames = branches.map((x) => x.name);

const pagination = getPagination(headers, pageLinks, repoInfo.page);

return {
branches: branchNames,
pagination: getPagination(headers, repoInfo.page)
pagination: pagination
};
}, req, res, logger);

const models = (req, res) => responseWrapper.sendResponseAsync(async () => {
const repository = repositories.get();

const branchInfo = {
organisation: req.params.organisation,
repo: req.params.repo,
Expand All @@ -70,6 +86,7 @@ const models = (req, res) => responseWrapper.sendResponseAsync(async () => {
}, req, res, logger);

const model = (req, res) => responseWrapper.sendResponseAsync(async () => {
const repository = repositories.get();
const modelInfo = {
organisation: req.params.organisation,
repo: req.params.repo,
Expand All @@ -83,6 +100,8 @@ const model = (req, res) => responseWrapper.sendResponseAsync(async () => {
}, req, res, logger);

const create = async (req, res) => {
const repository = repositories.get();

const modelBody = {
organisation: req.params.organisation,
repo: req.params.repo,
Expand All @@ -102,6 +121,8 @@ const create = async (req, res) => {
};

const update = async (req, res) => {
const repository = repositories.get();

const modelBody = {
organisation: req.params.organisation,
repo: req.params.repo,
Expand All @@ -121,6 +142,8 @@ const update = async (req, res) => {
};

const deleteModel = async (req, res) => {
const repository = repositories.get();

const modelInfo = {
organisation: req.params.organisation,
repo: req.params.repo,
Expand All @@ -138,10 +161,28 @@ const deleteModel = async (req, res) => {
}
};

const getPagination = (headers, page) => {
const pagination = { page, next: false, prev: false };
const linkHeader = headers.link;
const getPagination = (headers, pageLinks, page) => {

if(headers === undefined || headers === null || (Object.keys(headers).length === 0) || headers?.link === null){
if (pageLinks === undefined || pageLinks === null || (Object.keys(pageLinks).length === 0)) {
return {page, next: false, prev: false};
}
return getPaginationFromPageLinks(pageLinks, page);
}
return getPaginationFromHeaders(headers, page);

};

const getPaginationFromPageLinks = (pageLinks, page) => {
const pagination = {page, next: false, prev: false};
pagination.next = pageLinks.next;
pagination.prev = pageLinks.prev;
return pagination;
};

const getPaginationFromHeaders = (headers, page) => {
const pagination = {page, next: false, prev: false};
const linkHeader = headers.link;
if (linkHeader) {
linkHeader.split(',').forEach((link) => {
const isLinkType = (type) => link.split(';')[1].split('=')[1] === type;
Expand All @@ -155,7 +196,6 @@ const getPagination = (headers, page) => {
}
});
}

return pagination;
};

Expand Down
27 changes: 27 additions & 0 deletions td.server/src/env/Bitbucket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Env } from './Env.js';

class BitbucketEnv extends Env {
constructor () {
super('Bitbucket');
}

get prefix () {
return 'BITBUCKET_';
}

get properties () {
return [
{ key: 'CLIENT_ID', required: false },
{ key: 'CLIENT_SECRET', required: false },
{ key: 'SCOPE', required: false },
{ key: 'ENTERPRISE_HOSTNAME', required: false },
{ key: 'ENTERPRISE_PROTOCOL', required: false },
{ key: 'ENTERPRISE_PORT', required: false },
{ key: 'USE_SEARCH', required: false },
{ key: 'SEARCH_QUERY', required: false },
{ key: 'WORKSPACE', required: false }
];
}
}

export default BitbucketEnv;
4 changes: 2 additions & 2 deletions td.server/src/env/Github.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class GithubEnv extends Env {

get properties () {
return [
{ key: 'CLIENT_ID', required: true },
{ key: 'CLIENT_SECRET', required: true },
{ key: 'CLIENT_ID', required: false },
{ key: 'CLIENT_SECRET', required: false },
{ key: 'SCOPE', required: false },
{ key: 'ENTERPRISE_HOSTNAME', required: false },
{ key: 'ENTERPRISE_PROTOCOL', required: false },
Expand Down
Loading

0 comments on commit 70fad8e

Please sign in to comment.