diff --git a/.cfformat.json b/.cfformat.json new file mode 100644 index 0000000..0233515 --- /dev/null +++ b/.cfformat.json @@ -0,0 +1,62 @@ +{ + "array.empty_padding": false, + "array.padding": true, + "array.multiline.min_length": 40, + "array.multiline.element_count": 2, + "array.multiline.leading_comma.padding": true, + "array.multiline.leading_comma": false, + "alignment.consecutive.assignments": true, + "alignment.consecutive.properties": true, + "alignment.consecutive.params": true, + "brackets.padding": true, + "comment.asterisks": "align", + "binary_operators.padding": true, + "for_loop_semicolons.padding": true, + "function_call.empty_padding": false, + "function_call.padding": true, + "function_call.multiline.leading_comma.padding": true, + "function_call.casing.builtin": "cfdocs", + "function_call.casing.userdefined": "camel", + "function_call.multiline.element_count": 2, + "function_call.multiline.leading_comma": false, + "function_call.multiline.min_length": 40, + "function_declaration.padding": true, + "function_declaration.empty_padding": false, + "function_declaration.multiline.leading_comma": false, + "function_declaration.multiline.leading_comma.padding": true, + "function_declaration.multiline.element_count": 2, + "function_declaration.multiline.min_length": 40, + "function_declaration.group_to_block_spacing": "compact", + "function_anonymous.empty_padding": false, + "function_anonymous.group_to_block_spacing": "compact", + "function_anonymous.multiline.element_count": 2, + "function_anonymous.multiline.leading_comma": false, + "function_anonymous.multiline.leading_comma.padding": true, + "function_anonymous.multiline.min_length": 40, + "function_anonymous.padding": true, + "indent_size": 4, + "keywords.block_to_keyword_spacing": "spaced", + "keywords.group_to_block_spacing": "spaced", + "keywords.padding_inside_group": true, + "keywords.spacing_to_block": "spaced", + "keywords.spacing_to_group": true, + "keywords.empty_group_spacing": false, + "max_columns": 120, + "metadata.multiline.element_count": 3, + "metadata.multiline.min_length": 40, + "method_call.chain.multiline" : 3, + "newline":"\n", + "property.multiline.element_count": 3, + "property.multiline.min_length": 40, + "parentheses.padding": true, + "strings.quote": "double", + "strings.attributes.quote": "double", + "struct.separator": " : ", + "struct.padding": true, + "struct.empty_padding": false, + "struct.multiline.leading_comma": false, + "struct.multiline.leading_comma.padding": true, + "struct.multiline.element_count": 2, + "struct.multiline.min_length": 40, + "tab_indent": true +} \ No newline at end of file diff --git a/.cflintrc b/.cflintrc new file mode 100644 index 0000000..fbe418e --- /dev/null +++ b/.cflintrc @@ -0,0 +1,62 @@ +{ + "rule": [], + "includes": [ + { "code": "AVOID_USING_CFINCLUDE_TAG" }, + { "code": "AVOID_USING_CFABORT_TAG" }, + { "code": "AVOID_USING_CFEXECUTE_TAG" }, + { "code": "AVOID_USING_DEBUG_ATTR" }, + { "code": "AVOID_USING_ABORT" }, + { "code": "AVOID_USING_ISDATE" }, + { "code": "AVOID_USING_ISDEBUGMODE" }, + { "code": "AVOID_USING_CFINSERT_TAG" }, + { "code": "AVOID_USING_CFUPDATE_TAG" }, + { "code": "ARG_VAR_CONFLICT" }, + { "code": "ARG_VAR_MIXED" }, + { "code": "ARG_HINT_MISSING" }, + { "code": "ARG_HINT_MISSING_SCRIPT" }, + { "code" : "ARGUMENT_INVALID_NAME" }, + { "code" : "ARGUMENT_ALLCAPS_NAME" }, + { "code" : "ARGUMENT_TOO_WORDY" }, + { "code" : "ARGUMENT_IS_TEMPORARY" }, + { "code": "CFQUERYPARAM_REQ" }, + { "code": "COMPARE_INSTEAD_OF_ASSIGN" }, + { "code": "COMPONENT_HINT_MISSING" }, + { "code" : "COMPONENT_INVALID_NAME" }, + { "code" : "COMPONENT_ALLCAPS_NAME" }, + { "code" : "COMPONENT_TOO_SHORT" }, + { "code" : "COMPONENT_TOO_LONG" }, + { "code" : "COMPONENT_TOO_WORDY" }, + { "code" : "COMPONENT_IS_TEMPORARY" }, + { "code" : "COMPONENT_HAS_PREFIX_OR_POSTFIX" }, + { "code": "COMPLEX_BOOLEAN_CHECK" }, + { "code": "EXCESSIVE_FUNCTION_LENGTH" }, + { "code": "EXCESSIVE_COMPONENT_LENGTH" }, + { "code": "EXCESSIVE_ARGUMENTS" }, + { "code": "EXCESSIVE_FUNCTIONS" }, + { "code": "EXPLICIT_BOOLEAN_CHECK" }, + { "code": "FUNCTION_TOO_COMPLEX" }, + { "code": "FUNCTION_HINT_MISSING" }, + { "code": "FILE_SHOULD_START_WITH_LOWERCASE" }, + { "code": "LOCAL_LITERAL_VALUE_USED_TOO_OFTEN" }, + { "code": "GLOBAL_LITERAL_VALUE_USED_TOO_OFTEN" }, + { "code": "MISSING_VAR" }, + { "code" : "METHOD_INVALID_NAME" }, + { "code" : "METHOD_ALLCAPS_NAME" }, + { "code" : "METHOD_IS_TEMPORARY" }, + { "code": "NESTED_CFOUTPUT" }, + { "code": "NEVER_USE_QUERY_IN_CFM" }, + { "code": "OUTPUT_ATTR" }, + { "code" : "QUERYPARAM_REQ" }, + { "code": "UNUSED_LOCAL_VARIABLE" }, + { "code": "UNUSED_METHOD_ARGUMENT" }, + { "code": "SQL_SELECT_STAR" }, + { "code": "SCOPE_ALLCAPS_NAME" }, + { "code": "VAR_ALLCAPS_NAME" }, + { "code": "VAR_INVALID_NAME" }, + { "code": "VAR_TOO_WORDY" } + ], + "inheritParent": false, + "parameters": { + "TooManyFunctionsChecker.maximum" : 20 + } +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a2c8081 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# http://editorconfig.org + +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false +indent_style = tab +indent_size = 4 +tab_width = 4 + +[*.yml] +indent_style = space +indent_size = 2 + +[*.{md,markdown}] +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 6846c75..412eeda 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,13 +10,13 @@ *.dbproj merge=union # Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 09dd4c5..ddc3087 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,14 @@ -# IDE Stuff -.settings -settings.xml +.vscode -# Logs -logs/*.log +.artifacts/** +.tmp/** -# Test Results -tests/results/* +test-harness/.engine/** +test-harness/coldbox/** +test-harness/docbox/** +test-harness/testbox/** +test-harness/logs/** +test-harness/modules/** -# Dependenncies -coldbox/* -testbox/* -artifacts/* -apidocs/docbox/* -workbench/* -build/* -jBCrypt-*/* -modules/* -!modules/bcrypt \ No newline at end of file +# log files +logs/** \ No newline at end of file diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..31705fa --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,14 @@ +{ + "line-length": false, + "single-h1": false, + "no-hard-tabs" : false, + "fenced-code-language" : false, + "no-bare-urls" : false, + "first-line-h1": false, + "no-multiple-blanks": { + "maximum": 2 + }, + "no-duplicate-header" : { + "siblings_only" : true + } +} \ No newline at end of file diff --git a/.module.properties b/.module.properties deleted file mode 100644 index 4ceba3b..0000000 --- a/.module.properties +++ /dev/null @@ -1,3 +0,0 @@ -project.name=cbox-bcrypt -project.version=2.5.0 -module.name=bcrypt \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e3e0dbb..588c7db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,13 @@ notifications: secure: FIHlTn/YO7Wgumm1uIqmoEsqjQA7fV0AE94Rjc5yKzM3AquQa8HicgDVVk0d2GrKRnl0xt3j4ZJV//VJyIjlCd/QVKuj48R2ChjEY2im3+99HFPafCUI5/S2uyowKU6mJTFonH9v6p41eqxdbiAxJdDGOT0V2Gpt3UBSNuHz8ED9/aIHqv+P7M+VD6Xd2XYwctPniWlaSWx57sWcnG/VkFG45qFQAyha64uxOOe4M3ZmG/n5FfauZ8cBVLiRKEIr+CyNhh1ujfzi7+4uzMlSNL5t/BbZamAQuZzqGzGQ9RVvIlyPgUGNJtDEE/hWS09aagXF5T6EMj00szizErh4J1/x4qZwml5+TcBN31E0QmAhCtZe85sr3tYgic+hEz9XX1yymQzf/C7n4to2yNvq0r4g51xDk8IuP95WEh7zaqLlvFZvBFgxpHZBMYlRvhytjOYDeIFRMcGwHZcXosaG2ejqDwcGq/LC4oeG4sSwmg9sdRrtcmcanrNqrBka86WYO6LntI3JdZ86/1ACEUHzhCCwvrKELc9Ji1xxGAgS7QKH+s2/hnJuiMyv73gOVLKYC+wPMLt+fvOmPLSEl+PJiAIlToBq1KUBg03RSQLfPOLD7OrJ8VvDZsEPwejqlGDyc4wRglS9OTi7SnN5LYHSDNDdGdREegWqq9qDHEYEVLI= env: + # Fill out these global variables for build process global: - - MODULE_ID=cbox-bcrypt + - MODULE_ID=bcrypt matrix: - - ENGINE=lucee@4.5 - ENGINE=lucee@5 - - ENGINE=adobe@10 - - ENGINE=adobe@11 - ENGINE=adobe@2016 + - ENGINE=adobe@2018 branches: only: @@ -23,82 +22,107 @@ dist: trusty sudo: required -cache: - directories: - - $HOME/.CommandBox - before_install: # CommandBox Keys - - sudo apt-key adv --keyserver keys.gnupg.net --recv 6DA70622 - - sudo echo "deb http://downloads.ortussolutions.com/debs/noarch /" | sudo tee -a + - curl -fsSl https://downloads.ortussolutions.com/debs/gpg | sudo apt-key add - + - sudo echo "deb https://downloads.ortussolutions.com/debs/noarch /" | sudo tee -a /etc/apt/sources.list.d/commandbox.list install: # Install Commandbox - - sudo apt-get update && sudo apt-get --assume-yes install git haveged rsync commandbox - # Test that the box binary is available and ready for our tests - - box version + - sudo apt-get update && sudo apt-get --assume-yes install rsync jq commandbox + # Install CommandBox Supporting Librarires + - box install commandbox-cfconfig,commandbox-dotenv,commandbox-docbox # If using auto-publish, you will need to provide your API token with this line: - box config set endpoints.forgebox.APIToken=$FORGEBOX_API_TOKEN > /dev/null - # Setup for our tests - - mkdir tests/results - - sudo chmod -R 775 tests/results -# Build script - note module versioning is passed to ANT script: + # Set Current Version and Travis Tag + - TARGET_VERSION=`cat $TRAVIS_BUILD_DIR/box.json | jq '.version' -r` + - TRAVIS_TAG=${TARGET_VERSION} + - echo "Starting build for ${MODULE_ID} v${TARGET_VERSION}" + # Replace version so builder can issue it + - box package set version=@build.version@+@build.number@ + # Startup the harness + - cd test-harness # run our dependency install to ensure the workbench is in place - box install - # add our module-specific build properties - - printf "\nmodule.name=$MODULE_ID" >> workbench/build.properties - - printf "\ncfengine=$ENGINE" >> workbench/build.properties - # execute our build - - ant -DisTravis=true -Dbuild.number=$TRAVIS_BUILD_NUMBER -Dbuild.branch=$TRAVIS_BRANCH -f workbench/build.xml + # run our matrix server + - box server start serverConfigFile="server-${ENGINE}.json" + # Startup the app + #- curl http://localhost:60299 + # Debugging of tests + #- curl http://localhost:60299/tests/runner.cfm?reporter=json -o testresults.json && cat testresults.json + # move back to build dir to build it + - cd $TRAVIS_BUILD_DIR + # Build Project + - box task run taskfile=build/Build target=run :version=${TARGET_VERSION} :projectName=${MODULE_ID} :buildID=${TRAVIS_BUILD_NUMBER} :branch=${TRAVIS_BRANCH} + # Cat results for debugging + #- cat build/results.json after_failure: - - cd $TRAVIS_BUILD_DIR + # Cat results just in case for debugging. + - curl http://localhost:60299 # Display the contents of our root directory + - cd $TRAVIS_BUILD_DIR/test-harness # Spit out our Commandbox log in case we need to debug - - box server log name=$ENGINE + - box server log server-${ENGINE}.json - cat `box system-log` - -before_deploy: - - cd $TRAVIS_BUILD_DIR - - mkdir -p s3deploy - - rsync -av ./artifacts/$MODULE_ID/ ./s3deploy/ - - rm -f ./s3deploy/box-repo.json deploy: - #Module Deployment + # Module Deployment - provider: s3 on: - branch: + branch: - master - development - condition: "$ENGINE = lucee@4.5" + condition: "$ENGINE = lucee@5" skip_cleanup: true #AWS Credentials need to be set in Travis access_key_id: $AWS_ACCESS_KEY secret_access_key: $AWS_ACCESS_SECRET + # Destination bucket: "downloads.ortussolutions.com" - local-dir: s3deploy + local-dir: $TRAVIS_BUILD_DIR/.artifacts/$MODULE_ID upload-dir: ortussolutions/coldbox-modules/$MODULE_ID acl: public_read - #API Docs Deployment + + # API Docs Deployment - provider: s3 on: - branch: + branch: - master - development - condition: "$ENGINE = lucee@4.5" + condition: "$ENGINE = lucee@5" skip_cleanup: true #AWS Credentials need to be set in Travis access_key_id: $AWS_ACCESS_KEY secret_access_key: $AWS_ACCESS_SECRET bucket: "apidocs.ortussolutions.com" - local-dir: build/apidocs - upload-dir: coldbox-modules/$MODULE_ID + local-dir: $TRAVIS_BUILD_DIR/.tmp/apidocs + upload-dir: coldbox-modules/$MODULE_ID/$TARGET_VERSION acl: public_read + # Github Release only on Master + - provider: releases + api_key: ${GITHUB_TOKEN} + on: + branch: + - master + condition: "$ENGINE = lucee@5" + skip_cleanup: true + edge: true + file_glob: true + file: $TRAVIS_BUILD_DIR/.artifacts/$MODULE_ID/**/* + release_notes_file: changelog.md + name: v${TRAVIS_TAG} + tag_name: v${TRAVIS_TAG} + overwrite: true +# Once API Docs and Binaries are deployed to S3 Publish to ForgeBox after_deploy: - - cd $TRAVIS_BUILD_DIR/build && box forgebox publish + # Move to build out artifact + - cd ${TRAVIS_BUILD_DIR}/.tmp/${MODULE_ID} + - cat box.json + # Only publish once using the lucee matrix + - if [ ${ENGINE} = 'lucee@5' ]; then box forgebox publish; fi diff --git a/modules/bcrypt/LICENSE b/LICENSE similarity index 100% rename from modules/bcrypt/LICENSE rename to LICENSE diff --git a/ModuleConfig.cfc b/ModuleConfig.cfc new file mode 100644 index 0000000..a2d1c93 --- /dev/null +++ b/ModuleConfig.cfc @@ -0,0 +1,39 @@ +/** + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * --- + * Module Configuration + */ +component { + + // Module Properties + this.title = "bcrypt"; + this.author = "Ortus Solutions, Corp"; + this.webURL = "https://www.ortussolutions.com"; + this.description = "This module provides a way to do bcrypt encryption in ColdBox apps"; + this.modelNamespace = "bcrypt"; + this.cfmapping = "bcrypt"; + // Module Dependencies That Must Be Loaded First, use internal names or aliases + this.dependencies = [ "cbjavaloader" ]; + // Helpers + this.applicationHelper = [ "helpers/mixins.cfm" ]; + + /** + * Module Config + */ + function configure(){ + // Module Settings + settings = { + libPath : modulePath & "/models/lib", + workFactor : 12 + }; + } + + /** + * Fired when the module is registered and activated. + */ + function onLoad(){ + // Class load bcrypt + wireBox.getInstance( "loader@cbjavaloader" ).appendPaths( settings.libPath ); + } + +} diff --git a/README.md b/README.md index fef7f77..cff207a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# BCrypt Module for the Coldbox Framework [![Build Status](https://travis-ci.org/coldbox-modules/cbox-bcrypt.svg?branch=master)](https://travis-ci.org/coldbox-modules/cbox-bcrypt) +# BCrypt Module for the ColdBox Framework [![Build Status](https://travis-ci.org/coldbox-modules/bcrypt.svg?branch=development)](https://travis-ci.org/coldbox-modules/bcrypt) BCrypt's primary usage would be for the secure hashing of passwords. The hashing method provides a high level of security, but also makes it too slow use as a simple digest. It is also not reversible, and therefore is not suitable for encrypting transmission data. - More information about BCrypt: * http://en.wikipedia.org/wiki/Bcrypt * http://bcrypt.sourceforge.net/ +* http://www.mindrot.org/projects/jBCrypt/ ## Installation @@ -18,18 +18,18 @@ box install bcrypt ## Requirements -* ColdBox Framework 4.0+ -* cbjavaloader module ( automatically installed as a dependency by Forgebox ) +* ColdBox Framework 5.0+ +* `cbjavaloader` module ( automatically installed as a dependency by Forgebox ) ## BCrypt.jar -A compiled version (0.4) of jBCrypt is listed as a dependency for the test harness. You can update the version by following the steps below. + +A compiled version (0.4) of **jBCrypt** is listed as a dependency for the test harness. You can update the version by following the steps below. 1. Visit http://www.mindrot.org/projects/jBCrypt/. 2. Copy the link to the latest version -3. Update the `tests/box.json` dependency with the correct version information -4. Run `box install` from within the `tests` directory -5. Execute `ant -f tests/resources/jBCrypt-[version number]/build.xml` and move the generated `jbcrypt.jar` file to `modules/bcrypt/models/lib` - +3. Update the `test-harness/box.json` dependency with the correct version information +4. Run `box install` from within the `test-harness` directory +5. Execute `ant -f test-harness/resources/jBCrypt-[version number]/build.xml` and move the generated `jbcrypt.jar` file to `models/lib` ## Usage @@ -39,13 +39,55 @@ A compiled version (0.4) of jBCrypt is listed as a dependency for the test harne This module registers a wirebox mapping to the Bcrypt singleton, `BCrypt@BCrypt`, which you may inject or instantiate in your componets: ```js +// Long Format property name="BCrypt" inject="BCrypt@BCrypt"; + +// Module Alias Shortcut +property name="BCrypt" inject="@BCrypt"; ``` or via `getInstance()` ( a ColdBox framework supertype method ) inside your handlers, views, interceptors, etc. ```js getInstance( "BCrypt@BCrypt" ) +getInstance( "@BCrypt" ) +``` + +### BCrypt Mixins + +We have also created three mixin helpers that will be injected to all your handlers, interceptors, layouts and views: `bcryptHash(), bcryptCheck(), bcryptSalt()` + +```js +/** + * Hashes an incoming input string according to work factor and salt + * + * @password The input password to encrypt + * @workFactor Optional work factor + * + * @return The bcrypted password + */ +string function bcryptHash( + required string password, + workFactor, + salt +) + +/** + * Check if the incoming candidate is the same as a bcrypthash, usually the best check for comparing them. + * + * @candidate The plain text string to compare against the encrypted hash + * @bCryptHash The bCrypt hash to compare it to + * + * @return True - if the match, false if they dont! + */ +boolean function bcryptCheck( required string candidate, required string bCryptHash ) + +/** + * Generates a salt for you. + * + * @workFactor The workfactor to use for the salt, by default we use the one in the settings + */ +string function bcryptSalt( workFactor ) ``` ### Generating a password hash @@ -53,15 +95,26 @@ getInstance( "BCrypt@BCrypt" ) The hashed password should be persisted so candidate passwords (submitted from login) can be checked against. ```js -var hashedPassword = getInstance( "BCrypt@BCrypt" ).hashPassword( plaintextPassword ); +var hashedPassword = getInstance( "BCrypt" ).hashPassword( plaintextPassword ); ``` - + ### Checking a password hash The `plaintextPasswordCandidate` is the password the user submits for authentication. The hashed password is retrieved for the user being authenticated. ```js -var isSamePassword = getInstance( "BCrypt@BCrypt" ).checkPassword( plaintextPasswordCandidate, hashedPassword ); +var isSamePassword = getInstance( "BCrypt" ).checkPassword( plaintextPasswordCandidate, hashedPassword ); +``` + +### Using a Custom Salt + +Internally we generate a `salt` for you according to the default work factor. You can however, alter this and pass in your own salt: + +```js +var hashedPassword = getInstance( "BCrypt" ).hashPassword( + password : plaintextPassword, + salt : mySalt +); ``` ### Configuring WorkFactor @@ -71,24 +124,22 @@ var isSamePassword = getInstance( "BCrypt@BCrypt" ).checkPassword( plaintextPass You can also set the workFactor on a per-call basis by passing it in as a second parameter to the `hashPassword` method like so: ```js -var hashedPassword = getInstance( "BCrypt@BCrypt" ).hashPassword( plaintextPassword, 7 ); +var hashedPassword = getInstance( "@BCrypt" ).hashPassword( plaintextPassword, 7 ); ``` ### BCrypt Settings -You may override the default work factor by creating a `BCrypt` settings struct in your `ColdBox.cfc`: +You may override the default work factor by creating a `BCrypt` settings struct in your `ColdBox.cfc` under the `moduleSettings` struct: ```js -BCrypt = { - workFactor = 12 +moduleSettings = { + bcrypt = { + workFactor = 15 + } }; ``` -You can read more about this module here: https://github.com/coldbox-modules/cbox-bcrypt/wiki - - - ******************************************************************************** Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp www.coldbox.org | www.luismajano.com | www.ortussolutions.com diff --git a/apidocs/Application.cfc b/apidocs/Application.cfc deleted file mode 100755 index ba94974..0000000 --- a/apidocs/Application.cfc +++ /dev/null @@ -1,20 +0,0 @@ -component{ - - this.name = "APIDocs" & hash(getCurrentTemplatePath()); - this.sessionManagement = true; - this.sessionTimeout = createTimeSpan(0,0,1,0); - - // API Root - API_ROOT = getDirectoryFromPath( getCurrentTemplatePath() ); - rootPath = REReplaceNoCase( API_ROOT, "apidocs(\\|\/)$", "" ); - - // MODULE NAME - request.moduleName = "bcrypt"; - - this.mappings[ "/docbox" ] = API_ROOT & "docbox"; - this.mappings[ "/root" ] = rootPath; - this.mappings[ "/#request.moduleName#" ] = rootPath & "modules/#request.moduleName#/models"; - - - -} \ No newline at end of file diff --git a/apidocs/box.json b/apidocs/box.json deleted file mode 100644 index 00c3fed..0000000 --- a/apidocs/box.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name":"API Docs", - "version":"1.0.0", - "slug":"module-apidocs", - "private":true, - "dependencies":{ - "docbox":"^2.0.7+00005" - }, - "devDependencies":{ - - }, - "installPaths":{ - "docbox":"docbox" - } -} \ No newline at end of file diff --git a/apidocs/index.cfm b/apidocs/index.cfm deleted file mode 100644 index 79d5ab6..0000000 --- a/apidocs/index.cfm +++ /dev/null @@ -1,22 +0,0 @@ - - - - docName = "#request.moduleName#-APIDocs"; - base = expandPath( "/#request.moduleName#" ); - docbox = new docbox.DocBox( properties = { - projectTitle = "#request.moduleName# v#url.version#", - outputDir = url.path - } ); - docbox.generate( source=base, mapping=request.moduleName ); - - - - - -

Done!

-Go to Docs! -
- diff --git a/apidocs/server.json b/apidocs/server.json deleted file mode 100644 index c37e697..0000000 --- a/apidocs/server.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "force":true, - "openbrowser":"false", - "web":{ - "http":{ - "port":"8511" - } - } -} \ No newline at end of file diff --git a/box.json b/box.json index c422d7d..4f4f5c1 100644 --- a/box.json +++ b/box.json @@ -1,31 +1,36 @@ { - "name":"BCrypt Test Harness", - "version":"1.0.0", - "author":"Ortus Solutions", - "contributors":[ + "name" : "BCrypt", + "version" : "3.0.0", + "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/bcrypt/@build.version@/bcrypt-@build.version@.zip", + "author" : "Ortus Solutions ", + "homepage" : "https://github.com/coldbox-modules/bcrypt", + "documentation" : "https://github.com/coldbox-modules/bcrypt", + "repository" :{ + "type" : "git", + "url" : "https://github.com/coldbox-modules/bcrypt" + }, + "bugs" : "https://ortussolutions.atlassian.net/secure/RapidBoard.jspa?projectKey=CCM", + "slug" : "BCrypt", + "shortDescription": "A ColdBox module for BCrypt. Creates cryptographically strong (and slow) password hashes.", + "type" : "modules", + "contributors" :[ "Seth Feldkamp", "Brad Wood ", "Luis Majano ", "Jon Clausen " ], - "devDependencies":{ - "cbjavaloader":"^1.1.0", - "testbox":"^2.3.0+00044", - "coldbox":"^4.2.0+00002", - "jBCrypt-0.4":"http://www.mindrot.org/files/jBCrypt/jBCrypt-0.4.zip", - "workbench":"git+https://github.com/Ortus-Solutions/unified-workbench.git" - }, - "installPaths":{ - "coldbox":"coldbox", - "testbox":"testbox", - "cbjavaloader":"modules/cbjavaloader", - "jBCrypt-0.4":"jBCrypt-0.4", - "workbench":"workbench" - }, - "testbox":{ - "runner":"http://localhost:49616/tests/runner.cfm" - }, - "scripts":{ - "postVersion":"recipe workbench/bump.boxr" + "ignore":[ + "**/.*", + "tests", + "*/.md" + ], + "dependencies" :{ + "cbjavaloader": "^2.0.0" + }, + "scripts":{ + "release":"recipe build/release.boxr", + "format":"cfformat run *.cfc,models/,test-harness/tests/specs", + "format:watch":"cfformat watch *.cfc,models/,test-harness/tests/specs ./.cfformat.json", + "format:check":"cfformat check *.cfc,models/,test-harness/tests/specs" } } \ No newline at end of file diff --git a/build/Build.cfc b/build/Build.cfc new file mode 100644 index 0000000..cf74dff --- /dev/null +++ b/build/Build.cfc @@ -0,0 +1,272 @@ +/** + * Build process for ColdBox Modules + * Adapt to your needs. + */ +component{ + + /** + * Constructor + */ + function init(){ + // Setup Pathing + variables.cwd = getCWD().reReplace( "\.$", "" ); + variables.artifactsDir = cwd & "/.artifacts"; + variables.buildDir = cwd & "/.tmp"; + variables.apiDocsURL = "http://localhost:60299/apidocs/"; + variables.testRunner = "http://localhost:60299/tests/runner.cfm"; + + // Source Excludes Not Added to final binary + variables.excludes = [ + ".gitignore", + ".travis.yml", + ".artifacts", + ".tmp", + "build", + "test-harness", + ".DS_Store", + ".git" + ]; + + // Cleanup + Init Build Directories + [ variables.buildDir, variables.artifactsDir ].each( function( item ){ + if( directoryExists( item ) ){ + directoryDelete( item, true ); + } + // Create directories + directoryCreate( item, true, true ); + } ); + + // Create Mappings + fileSystemUtil.createMapping( "coldbox", variables.cwd & "test-harness/coldbox" ); + + return this; + } + + /** + * Run the build process: test, build source, docs, checksums + * + * @projectName The project name used for resources and slugs + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function run( + required projectName, + version="1.0.0", + buildID=createUUID(), + branch="development" + ){ + // Create project mapping + fileSystemUtil.createMapping( arguments.projectName, variables.cwd ); + + // Run the tests + runTests(); + + // Build the source + buildSource( argumentCollection=arguments ); + + // Build Docs + arguments.outputDir = variables.buildDir & "/apidocs"; + docs( argumentCollection=arguments ); + + // checksums + buildChecksums(); + + // Build latest changelog + latestChangelog(); + + // Finalize Message + print.line() + .boldMagentaLine( "Build Process is done! Enjoy your build!" ) + .toConsole(); + } + + /** + * Run the test suites + */ + function runTests(){ + // Tests First, if they fail then exit + print.blueLine( "Testing the package, please wait..." ).toConsole(); + + command( 'testbox run' ) + .params( + runner = variables.testRunner, + verbose = true, + outputFile = "build/results.json" + ) + .run(); + + // Check Exit Code? + if( shell.getExitCode() ){ + return error( "Cannot continue building, tests failed!" ); + } + } + + /** + * Build the source + * + * @projectName The project name used for resources and slugs + * @version The version you are building + * @buldID The build identifier + * @branch The branch you are building + */ + function buildSource( + required projectName, + version="1.0.0", + buildID=createUUID(), + branch="development" + ){ + // Build Notice ID + print.line() + .boldMagentaLine( "Building #arguments.projectName# v#arguments.version#+#arguments.buildID# from #cwd# using the #arguments.branch# branch." ) + .toConsole(); + + // Prepare exports directory + variables.exportsDir = variables.artifactsDir & "/#projectName#/#arguments.version#"; + directoryCreate( variables.exportsDir, true, true ); + + // Project Build Dir + variables.projectBuildDir = variables.buildDir & "/#projectName#"; + directoryCreate( variables.projectBuildDir, true, true ); + + // Copy source + print.blueLine( "Copying source to build folder..." ).toConsole(); + copy( variables.cwd, variables.projectBuildDir ); + + // Create build ID + fileWrite( "#variables.projectBuildDir#/#projectName#-#version#+#buildID#", "Built with love on #dateTimeFormat( now(), "full")#" ); + + // Updating Placeholders + print.greenLine( "Updating version identifier to #arguments.version#" ).toConsole(); + command( 'tokenReplace' ) + .params( + path = "/#variables.projectBuildDir#/**", + token = "@build.version@", + replacement = arguments.version + ) + .run(); + + print.greenLine( "Updating build identifier to #arguments.buildID#" ).toConsole(); + command( 'tokenReplace' ) + .params( + path = "/#variables.projectBuildDir#/**", + token = ( arguments.branch == "master" ? "@build.number@" : "+@build.number@" ), + replacement = ( arguments.branch == "master" ? arguments.buildID : "-snapshot" ) + ) + .run(); + + // zip up source + var destination = "#variables.exportsDir#/#projectName#-#version#.zip"; + print.greenLine( "Zipping code to #destination#" ).toConsole(); + cfzip( + action="zip", + file="#destination#", + source="#variables.projectBuildDir#", + overwrite=true, + recurse=true + ); + + // Copy box.json for convenience + fileCopy( "#variables.projectBuildDir#/box.json", variables.exportsDir ); + } + + /** + * Produce the API Docs + */ + function docs( required projectName, version="1.0.0", outputDir=".tmp/apidocs" ){ + // Generate Docs + print.greenLine( "Generating API Docs, please wait..." ).toConsole(); + directoryCreate( arguments.outputDir, true, true ); + + command( 'docbox generate' ) + .params( + "source" = "models", + "mapping" = "models", + "strategy-projectTitle" = "#arguments.projectName# v#arguments.version#", + "strategy-outputDir" = arguments.outputDir + ) + .run(); + + print.greenLine( "API Docs produced at #arguments.outputDir#" ).toConsole(); + + var destination = "#variables.exportsDir#/#projectName#-docs-#version#.zip"; + print.greenLine( "Zipping apidocs to #destination#" ).toConsole(); + cfzip( + action="zip", + file="#destination#", + source="#arguments.outputDir#", + overwrite=true, + recurse=true + ); + } + + /** + * Build the latest changelog file: changelog-latest.md + */ + function latestChangelog(){ + print.blueLine( "Building latest changelog..." ).toConsole(); + + if( !fileExists( variables.cwd & "changelog.md" ) ){ + return error( "Cannot continue building, changelog.md file doesn't exist!" ); + } + + fileWrite( + variables.cwd & "changelog-latest.md", + fileRead( variables.cwd & 'changelog.md' ).split( '----' )[2].trim() & chr( 13 ) & chr( 10 ) + ); + + print + .greenLine( "Latest changelog file created at `changelog-latest.md`" ) + .line() + .line( fileRead( variables.cwd & "changelog-latest.md" ) ); + } + + /********************************************* PRIVATE HELPERS *********************************************/ + + /** + * Build Checksums + */ + private function buildChecksums(){ + print.greenLine( "Building checksums" ).toConsole(); + command( 'checksum' ) + .params( path = '#variables.exportsDir#/*.zip', algorithm = 'SHA-512', extension="sha512", write=true ) + .run(); + command( 'checksum' ) + .params( path = '#variables.exportsDir#/*.zip', algorithm = 'md5', extension="md5", write=true ) + .run(); + } + + /** + * DirectoryCopy is broken in lucee + */ + private function copy( src, target, recurse=true ){ + // process paths with excludes + directoryList( src, false, "path", function( path ){ + var isExcluded = false; + variables.excludes.each( function( item ){ + if( path.replaceNoCase( variables.cwd, "", "all" ).findNoCase( item ) ){ + isExcluded = true; + } + } ); + return !isExcluded; + }).each( function( item ){ + // Copy to target + if( fileExists( item ) ){ + print.blueLine( "Copying #item#" ).toConsole(); + fileCopy( item, target ); + } else { + print.greenLine( "Copying directory #item#" ).toConsole(); + directoryCopy( item, target & "/" & item.replace( src, "" ), true ); + } + } ); + } + + /** + * Gets the last Exit code to be used + **/ + private function getExitCode() { + return (createObject( 'java', 'java.lang.System' ).getProperty( 'cfml.cli.exitCode' ) ?: 0); + + } + +} \ No newline at end of file diff --git a/build/release.boxr b/build/release.boxr new file mode 100755 index 0000000..e216f22 --- /dev/null +++ b/build/release.boxr @@ -0,0 +1,25 @@ +# This recipe signifies a new release of the module by doing merges and bumps accordingly + +# Check out master and update it locally +!git checkout -f master +!git pull origin master + +# Merge development into it for release +!git merge --no-ff development + +# Tag the master repo with the version from box.json +!git tag v`box package show version` + +# Push all branches back out to github +!git push origin --all + +# Push all tags +!git push origin --tags + +# Check development again +!git checkout -f development + +# Bump to prepare for a new release, do minor, change if needed and don't tag +bump --minor --!tagVersion +!git commit -a -m "version bump" +!git push origin development \ No newline at end of file diff --git a/changelog.md b/changelog.md index c06026b..2158a61 100644 --- a/changelog.md +++ b/changelog.md @@ -1,11 +1,45 @@ # Changelog -## 2.5.0 +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +---- + +## [3.0.0] => 2020-NOV-1 + +### Added + +* Added three new helper mixins for easy usage : `bcryptHash(), bcryptCheck(), bcryptSalt()` +* New method to generate bcrypt salts: `generateSalt( workFactor = default ): bcryptSalt()` +* Ability to pass in a custom `salt` argument via the `hashPassword( input, workFactor, salt )` method +* `compatiblity` : New `moduleSettings` configuration as per ColdBox 5+ instead of parsing parent settings +* Updated to newest module layout +* Lots of docs for methods +* Upgraded tests to ColdBox 6+ + +### Fixed + +* Upgraded to cbJavaloader 2.x due to security issues in 1.x + +### Removed + +* ACF11 Support +* Lucee 4.5 Support + +---- + +## [2.5.0] => 2017-MAR-22 + * Updated BCrypt library to v0.4 * Fixes a memory leak when performing over 10 concurrent hashes * Module test harness updates -## 2.1.0 +---- + +## [2.1.0] => 2016-MAY-04 + * Updated Docs * Fixed javacast issue when passing a work factor * Updated JavaLoader dependencies diff --git a/config/.htaccess b/config/.htaccess deleted file mode 100644 index 3d45d68..0000000 --- a/config/.htaccess +++ /dev/null @@ -1,4 +0,0 @@ -#apache access file to protect the config.xml.cfm file. Delete this if you do not use apache. -authtype Basic -deny from all -Options -Indexes \ No newline at end of file diff --git a/config/Application.cfm b/config/Application.cfm deleted file mode 100644 index 50762fb..0000000 --- a/config/Application.cfm +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/config/Coldbox.cfc b/config/Coldbox.cfc deleted file mode 100644 index caad19c..0000000 --- a/config/Coldbox.cfc +++ /dev/null @@ -1,82 +0,0 @@ -component{ - - // Configure ColdBox Application - function configure(){ - - // coldbox directives - coldbox = { - //Application Setup - appName = "Development Shell", - - //Development Settings - reinitPassword = "", - handlersIndexAutoReload = true, - - //Implicit Events - defaultEvent = "main.index", - requestStartHandler = "", - requestEndHandler = "", - applicationStartHandler = "main.onAppInit", - applicationEndHandler = "", - sessionStartHandler = "", - sessionEndHandler = "", - missingTemplateHandler = "", - - //Extension Points - ApplicationHelper = "", - coldboxExtensionsLocation = "", - modulesExternalLocation = [], - pluginsExternalLocation = "", - viewsExternalLocation = "", - layoutsExternalLocation = "", - handlersExternalLocation = "", - requestContextDecorator = "", - - //Error/Exception Handling - exceptionHandler = "", - onInvalidEvent = "", - customErrorTemplate = "/coldbox/system/includes/BugReport.cfm", - - //Application Aspects - handlerCaching = false, - eventCaching = false, - proxyReturnCollection = false - }; - - // custom settings - settings = { - }; - - // Module Directives - modules = { - //Turn to false in production, on for dev - autoReload = false - }; - - //LogBox DSL - logBox = { - // Define Appenders - appenders = { - files={class="coldbox.system.logging.appenders.RollingFileAppender", - properties = { - filename = "app", filePath="/#appMapping#/logs" - } - } - }, - // Root Logger - root = { levelmax="DEBUG", appenders="*" }, - // Implicit Level Categories - info = [ "coldbox.system" ] - }; - - //Register interceptors as an array, we need order - interceptors = [ - //SES - {class="coldbox.system.interceptors.SES", - properties={} - } - ]; - - } - -} \ No newline at end of file diff --git a/config/Routes.cfm b/config/Routes.cfm deleted file mode 100644 index 9acad99..0000000 --- a/config/Routes.cfm +++ /dev/null @@ -1,20 +0,0 @@ - - // Allow unique URL or combination (false) - setUniqueURLS(false); - // Auto reload configuration, true in dev makes sense - //setAutoReload(false); - // Sets automatic route extension detection and places the extension in the rc.format - // setExtensionDetection(true) - // setValidExtensions('xml,json,jsont,rss,html,htm'); - - // Base URL - if( len(getSetting('AppMapping') ) lte 1){ - setBaseURL("http://#cgi.HTTP_HOST#/index.cfm"); - } - else{ - setBaseURL("http://#cgi.HTTP_HOST#/#getSetting('AppMapping')#/index.cfm"); - } - - // Your Application Routes - addRoute(pattern=":handler/:action?"); - \ No newline at end of file diff --git a/handlers/Main.cfc b/handlers/Main.cfc deleted file mode 100644 index faac417..0000000 --- a/handlers/Main.cfc +++ /dev/null @@ -1,16 +0,0 @@ -/** -* My Event Handler Hint -*/ -component{ - - // Index - any function index( event, rc, prc ){ - event.setView( "main/index" ); - } - - // Run on first init - any function onAppInit( event, rc, prc ){ - - } - -} \ No newline at end of file diff --git a/helpers/mixins.cfm b/helpers/mixins.cfm new file mode 100644 index 0000000..2f65451 --- /dev/null +++ b/helpers/mixins.cfm @@ -0,0 +1,39 @@ + + /** + * Hashes an incoming input string according to work factor and salt + * + * @password The input password to encrypt + * @workFactor Optional work factor + * + * @return The bcrypted password + */ + string function bcryptHash( + required string password, + workFactor, + salt + ){ + return getInstance( "@bcrypt" ).hashPassword( argumentCollection=arguments ); + } + + /** + * Check if the incoming candidate is the same as a bcrypthash, usually the best check for comparing them. + * + * @candidate The plain text string to compare against the encrypted hash + * @bCryptHash The bCrypt hash to compare it to + * + * @return True - if the match, false if they dont! + */ + boolean function bcryptCheck( required string candidate, required string bCryptHash ){ + return getInstance( "@bcrypt" ).checkPassword( argumentCollection=arguments ); + } + + /** + * Generates a salt for you. + * + * @workFactor The workfactor to use for the salt, by default we use the one in the settings + */ + string function bcryptSalt( workFactor ){ + return getInstance( "@bcrypt" ).generateSalt( argumentCollection=arguments ); + } + + \ No newline at end of file diff --git a/layouts/Main.cfm b/layouts/Main.cfm deleted file mode 100644 index 3188cb3..0000000 --- a/layouts/Main.cfm +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - cbox-bcrypt Test Harness - - - - - - - -
#renderView()#
- - -
diff --git a/models/.gitkeep b/models/.gitkeep deleted file mode 100644 index 3986db7..0000000 --- a/models/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -Reserved for appliciation modules \ No newline at end of file diff --git a/models/BCrypt.cfc b/models/BCrypt.cfc new file mode 100644 index 0000000..c4a1cfb --- /dev/null +++ b/models/BCrypt.cfc @@ -0,0 +1,110 @@ +/** + * Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp + * --- + * Hashes and passwords and checks password hashes using BCrypt.jar + */ +component singleton threadsafe{ + + // DI + property name="javaLoader" inject="loader@cbjavaloader"; + property name="settings" inject="coldbox:modulesettings:bcrypt"; + + /** + * Constructor + */ + BCrypt function init(){ + return this; + } + + /** + * On DI Complete load library + */ + function onDIComplete(){ + loadBCrypt(); + } + + /** + * Hashes an incoming input string according to work factor and salt + * + * @password The input password to encrypt + * @workFactor Optional work factor + * + * @return The bcrypted password + */ + string function hashPassword( + required string password, + workFactor=variables.settings.workFactor, + salt =generateSalt() + ){ + return variables.bcrypt.hashpw( arguments.password, arguments.salt ); + } + + /** + * Check if the incoming candidate is the same as a bcrypthash, usually the best check for comparing them. + * + * @candidate The plain text string to compare against the encrypted hash + * @bCryptHash The bCrypt hash to compare it to + * + * @return True - if the match, false if they dont! + */ + boolean function checkPassword( required string candidate, required string bCryptHash ){ + return variables.bcrypt.checkpw( arguments.candidate, arguments.bCryptHash ); + } + + /** + * Generates a salt for you. + * + * @workFactor The workfactor to use for the salt, by default we use the one in the settings + */ + string function generateSalt( workFactor=variables.settings.workFactor ){ + return variables.bcrypt.genSalt( javaCast( "int", arguments.workFactor ) ); + } + + /** + * Load the library + * + * @throws ClassNotFoundException - When bcrypt can't be classloaded + */ + private void function loadBCrypt(){ + + tryToLoadBCryptFromClassPath(); + + if( !isBCryptLoaded() ){ + tryToLoadBCryptWithJavaLoader(); + } + + if( !isBCryptLoaded() ){ + throw( + type : "ClassNotFoundException", + message : "BCrypt not successfully loaded. BCrypt.jar must be present in the ColdFusion classpath or at the setting javaloader_libpath. No operations are available." + ); + } + } + + /** + * Try to load if java lib in CF Path + */ + private void function tryToLoadBCryptFromClassPath(){ + try{ + variables.bcrypt = createObject( "java", "org.mindrot.jbcrypt.BCrypt" ); + } catch( any error ) { + } + } + + /** + * Load via module + */ + private void function tryToLoadBCryptWithJavaLoader(){ + try{ + variables.bcrypt = variables.javaLoader.create( "org.mindrot.jbcrypt.BCrypt" ); + } catch( any error ) { + } + } + + /** + * Is BCrypt loaded + */ + private boolean function isBCryptLoaded(){ + return !isNull( variables.bcrypt ); + } +} \ No newline at end of file diff --git a/modules/bcrypt/models/lib/jbcrypt.jar b/models/lib/jbcrypt.jar similarity index 100% rename from modules/bcrypt/models/lib/jbcrypt.jar rename to models/lib/jbcrypt.jar diff --git a/modules/bcrypt/ModuleConfig.cfc b/modules/bcrypt/ModuleConfig.cfc deleted file mode 100644 index e8f4131..0000000 --- a/modules/bcrypt/ModuleConfig.cfc +++ /dev/null @@ -1,58 +0,0 @@ -component { - - // Module Properties - this.modelNamespace = "bcrypt"; - this.cfmapping = "bcrypt"; - // Module Dependencies That Must Be Loaded First, use internal names or aliases - this.dependencies = [ "cbjavaloader" ]; - - function configure(){ - - //Skip information vars if the box.json file has been removed - if( fileExists( modulePath & '/box.json' ) ){ - //read in our box.json file for so we don't duplicate the information above - var moduleInfo = deserializeJSON( fileRead( modulePath & '/box.json' ) ); - - this.title = moduleInfo.name; - this.author = moduleInfo.author; - this.webURL = moduleInfo.homepage; - this.description = moduleInfo.shortDescription; - this.version = moduleInfo.version; - - } - - // Module Settings - settings = { - libPath = modulePath & "/models/lib" - }; - } - - /** - * Fired when the module is registered and activated. - */ - function onLoad(){ - // parse parent settings - parseParentSettings(); - // Class load antisamy - wireBox.getInstance( "loader@cbjavaloader" ) - .appendPaths( settings.libPath ); - } - - /** - * parse parent settings - */ - private function parseParentSettings(){ - var oConfig = controller.getSetting( "ColdBoxConfig" ); - var configStruct = controller.getConfigSettings(); - var bcryptDSL = oConfig.getPropertyMixin( "bCrypt", "variables", structnew() ); - - //defaults - configStruct.bCrypt = { - workFactor = 12 - }; - - // incorporate settings - structAppend( configStruct.bCrypt, bcryptDSL, true ); - } - -} \ No newline at end of file diff --git a/modules/bcrypt/box.json b/modules/bcrypt/box.json deleted file mode 100644 index 9180109..0000000 --- a/modules/bcrypt/box.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name" : "BCrypt", - "version" : "@build.version@+@build.number@", - "author" : "Ortus Solutions ", - "location" : "http://downloads.ortussolutions.com.s3.amazonaws.com/ortussolutions/coldbox-modules/cbox-bcrypt/@build.version@/cbox-bcrypt-@build.version@.zip", - "homepage" : "https://github.com/coldbox-modules/cbox-bcrypt", - "documentation" : "https://github.com/coldbox-modules/cbox-bcrypt", - "repository" :{ - "type" : "git", - "url" : "https://github.com/coldbox-modules/cbox-bcrypt" - }, - "bugs" : "https://ortussolutions.atlassian.net/secure/RapidBoard.jspa?projectKey=CCM", - "slug" : "BCrypt", - "shortDescription": "A ColdBox module for BCrypt. Creates cryptographically strong (and slow) password hashes.", - "type" : "modules", - "contributors" :[ - "Seth Feldkamp", - "Brad Wood ", - "Luis Majano ", - "Jon Clausen " - ], - "dependencies" :{ - "cbjavaloader": "^1.1.0" - }, - "ignore" :[] -} \ No newline at end of file diff --git a/modules/bcrypt/models/BCrypt.cfc b/modules/bcrypt/models/BCrypt.cfc deleted file mode 100644 index 29ecf0c..0000000 --- a/modules/bcrypt/models/BCrypt.cfc +++ /dev/null @@ -1,95 +0,0 @@ -/** -* Hashes and passwords and checks password hashes using BCrypt.jar -*/ -component singleton threadsafe{ - - // DI - property name="javaLoader" inject="loader@cbjavaloader"; - property name="settings" inject="coldbox:setting:bcrypt"; - - /** - * Constructor - */ - public BCrypt function init(){ - return this; - } - - /** - * On DI Complete load library - */ - public function onDIComplete(){ - loadBCrypt(); - } - - /** - * Hash a password - * @password The plain string password - * @workFactor Optional work factor - */ - public string function hashPassword( required string password, workFactor=variables.settings.workFactor ){ - var salt = variables.BCrypt.genSalt( javaCast( "int", arguments.workFactor ) ); - return variables.BCrypt.hashpw( password, salt ); - } - - /** - * Check a password - * @candidate incoming password - * @bCryptHash The bCrypt hash to compare - */ - public boolean function checkPassword( required string candidate, required string bCryptHash ){ - return variables.BCrypt.checkpw( candidate, bCryptHash ); - } - - /** - * Load the library - */ - private void function loadBCrypt(){ - - tryToLoadBCryptFromClassPath(); - - if( NOT isBCryptLoaded() ){ - - tryToLoadBCryptWithJavaLoader(); - - } - - if( NOT isBCryptLoaded() ){ - - throw( "BCrypt not successfully loaded. BCrypt.jar must be present in the ColdFusion classpath or at the setting javaloader_libpath. No operations are available." ); - - } - } - - /** - * Try to load if java lib in CF Path - */ - private void function tryToLoadBCryptFromClassPath(){ - try{ - - variables.bcrypt = createObject( "java", "org.mindrot.jbcrypt.BCrypt" ); - - } catch( any error ) { - - } - } - - /** - * Load via module - */ - private void function tryToLoadBCryptWithJavaLoader(){ - try{ - - variables.bcrypt = javaLoader.create( "org.mindrot.jbcrypt.BCrypt" ); - - } catch( any error ) { - - } - } - - /** - * Is BCrypt loaded - */ - private boolean function isBCryptLoaded(){ - return structKeyExists( variables, "BCrypt" ); - } -} \ No newline at end of file diff --git a/server.json b/server.json deleted file mode 100644 index af50471..0000000 --- a/server.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "force":true, - "openbrowser":"false", - "web":{ - "http":{ - "port":"49616" - } - } -} \ No newline at end of file diff --git a/test-harness/.cfconfig.json b/test-harness/.cfconfig.json new file mode 100644 index 0000000..6423922 --- /dev/null +++ b/test-harness/.cfconfig.json @@ -0,0 +1,9 @@ +{ + "debuggingEnabled":true, + "debuggingReportExecutionTimes":false, + "disableInternalCFJavaComponents":false, + "inspectTemplate":"always", + "requestTimeout":"0,0,0,90", + "robustExceptionEnabled":true, + "whitespaceManagement":"white-space-pref" +} \ No newline at end of file diff --git a/test-harness/.cflintrc b/test-harness/.cflintrc new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test-harness/.cflintrc @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Application.cfc b/test-harness/Application.cfc similarity index 51% rename from Application.cfc rename to test-harness/Application.cfc index e10f74c..6730042 100644 --- a/Application.cfc +++ b/test-harness/Application.cfc @@ -1,24 +1,51 @@ -/** +/** ******************************************************************************** Copyright 2005-2007 ColdBox Framework by Luis Majano and Ortus Solutions, Corp -www.coldboxframework.com | www.luismajano.com | www.ortussolutions.com +www.ortussolutions.com ******************************************************************************** */ component{ + + // UPDATE THE NAME OF THE MODULE IN TESTING BELOW + request.MODULE_NAME = "bcrypt"; + // Application properties - this.name = hash( getCurrentTemplatePath() ); + this.name = hash( getCurrentTemplatePath() ); this.sessionManagement = true; - this.sessionTimeout = createTimeSpan(0,0,30,0); - this.setClientCookies = true; + this.sessionTimeout = createTimeSpan(0,0,15,0); + this.setClientCookies = true; + + /************************************** + LUCEE Specific Settings + **************************************/ + // buffer the output of a tag/function body to output in case of a exception + this.bufferOutput = true; + // Activate Gzip Compression + this.compression = false; + // Turn on/off white space managemetn + this.whiteSpaceManagement = "smart"; + // Turn on/off remote cfc content whitespace + this.suppressRemoteComponentContent = false; // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP - COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() ); + COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() ); // The web server mapping to this application. Used for remote purposes or static purposes - COLDBOX_APP_MAPPING = ""; + COLDBOX_APP_MAPPING = ""; // COLDBOX PROPERTIES - COLDBOX_CONFIG_FILE = ""; + COLDBOX_CONFIG_FILE = ""; // COLDBOX APPLICATION KEY OVERRIDE - COLDBOX_APP_KEY = ""; + COLDBOX_APP_KEY = ""; + + // Mappings + this.mappings[ "/root" ] = COLDBOX_APP_ROOT_PATH; + + // Map back to its root + moduleRootPath = REReplaceNoCase( this.mappings[ "/root" ], "#request.MODULE_NAME#(\\|/)test-harness(\\|/)", "" ); + modulePath = REReplaceNoCase( this.mappings[ "/root" ], "test-harness(\\|/)", "" ); + + // Module Root + Path Mappings + this.mappings[ "/moduleroot" ] = moduleRootPath; + this.mappings[ "/#request.MODULE_NAME#" ] = modulePath; // application start public boolean function onApplicationStart(){ @@ -29,6 +56,7 @@ component{ // request start public boolean function onRequestStart(String targetPage){ + // Process ColdBox Request application.cbBootstrap.onRequestStart( arguments.targetPage ); diff --git a/test-harness/box.json b/test-harness/box.json new file mode 100644 index 0000000..25da938 --- /dev/null +++ b/test-harness/box.json @@ -0,0 +1,24 @@ +{ + "name":"Tester", + "version":"0.0.0", + "slug":"tester", + "private":true, + "description":"", + "dependencies":{ + "coldbox":"^6.0.0", + "cbjavaloader":"^2.0.0+49" + }, + "devDependencies":{ + "jBCrypt-0.4":"http://www.mindrot.org/files/jBCrypt/jBCrypt-0.4.zip", + "testbox":"^4.0.0" + }, + "installPaths":{ + "coldbox":"coldbox/", + "testbox":"testbox/", + "jBCrypt-0.4":"jBCrypt-0.4/", + "cbjavaloader":"modules/cbjavaloader/" + }, + "testbox":{ + "runner":"http://localhost:60299/tests/runner.cfm" + } +} \ No newline at end of file diff --git a/test-harness/config/Application.cfc b/test-harness/config/Application.cfc new file mode 100644 index 0000000..23c5ad0 --- /dev/null +++ b/test-harness/config/Application.cfc @@ -0,0 +1,7 @@ +/** +* This is a protection Application cfm for the config file. You do not +* need to modify this file +*/ +component{ + abort; +} \ No newline at end of file diff --git a/test-harness/config/Coldbox.cfc b/test-harness/config/Coldbox.cfc new file mode 100644 index 0000000..e0c9905 --- /dev/null +++ b/test-harness/config/Coldbox.cfc @@ -0,0 +1,84 @@ +component{ + + // Configure ColdBox Application + function configure(){ + + // coldbox directives + coldbox = { + //Application Setup + appName = "Module Tester", + + //Development Settings + reinitPassword = "", + handlersIndexAutoReload= true, + modulesExternalLocation= [], + + //Implicit Events + defaultEvent = "", + requestStartHandler = "", + requestEndHandler = "", + applicationStartHandler= "", + applicationEndHandler = "", + sessionStartHandler = "", + sessionEndHandler = "", + missingTemplateHandler = "", + + //Error/Exception Handling + exceptionHandler = "", + onInvalidEvent = "", + customErrorTemplate= "/coldbox/system/exceptions/whoops.cfm", + + //Application Aspects + handlerCaching= false, + eventCaching = false + }; + + // environment settings, create a detectEnvironment() method to detect it yourself. + // create a function with the name of the environment so it can be executed if that environment is detected + // the value of the environment is a list of regex patterns to match the cgi.http_host. + environments = { + development = "localhost,127\.0\.0\.1" + }; + + // Module Directives + modules = { + // An array of modules names to load, empty means all of them + include = [], + // An array of modules names to NOT load, empty means none + exclude = [] + }; + + //Register interceptors as an array, we need order + interceptors = [ + ]; + + //LogBox DSL + logBox = { + // Define Appenders + appenders = { + files={class="coldbox.system.logging.appenders.RollingFileAppender", + properties = { + filename = "tester", filePath="/#appMapping#/logs" + } + } + }, + // Root Logger + root = { levelmax="DEBUG", appenders="*" }, + // Implicit Level Categories + info = [ "coldbox.system" ] + }; + + } + + /** + * Load the Module you are testing + */ + function afterAspectsLoad( event, interceptData, rc, prc ){ + controller.getModuleService() + .registerAndActivateModule( + moduleName = request.MODULE_NAME, + invocationPath = "moduleroot" + ); + } + +} \ No newline at end of file diff --git a/test-harness/config/Routes.cfm b/test-harness/config/Routes.cfm new file mode 100644 index 0000000..a304d25 --- /dev/null +++ b/test-harness/config/Routes.cfm @@ -0,0 +1,46 @@ + + // Allow unique URL or combination of URLs, we recommend both enabled + setUniqueURLS(false); + // Auto reload configuration, true in dev makes sense to reload the routes on every request + //setAutoReload(false); + // Sets automatic route extension detection and places the extension in the rc.format variable + // setExtensionDetection(true); + // The valid extensions this interceptor will detect + // setValidExtensions('xml,json,jsont,rss,html,htm'); + // If enabled, the interceptor will throw a 406 exception that an invalid format was detected or just ignore it + // setThrowOnInvalidExtension(true); + + // Base URL + if( len(getSetting('AppMapping') ) lte 1){ + setBaseURL("http://#cgi.HTTP_HOST#/"); + } + else{ + setBaseURL("http://#cgi.HTTP_HOST#/#getSetting('AppMapping')#/"); + } + + // Your Application Routes + addRoute(pattern=":handler/:action?"); + + + /** Developers can modify the CGI.PATH_INFO value in advance of the SES + interceptor to do all sorts of manipulations in advance of route + detection. If provided, this function will be called by the SES + interceptor instead of referencing the value CGI.PATH_INFO. + + This is a great place to perform custom manipulations to fix systemic + URL issues your Web site may have or simplify routes for i18n sites. + + @Event The ColdBox RequestContext Object + **/ + function PathInfoProvider(Event){ + /* Example: + var URI = CGI.PATH_INFO; + if (URI eq "api/foo/bar") + { + Event.setProxyRequest(true); + return "some/other/value/for/your/routes"; + } + */ + return CGI.PATH_INFO; + } + \ No newline at end of file diff --git a/test-harness/config/WireBox.cfc b/test-harness/config/WireBox.cfc new file mode 100644 index 0000000..3aafc01 --- /dev/null +++ b/test-harness/config/WireBox.cfc @@ -0,0 +1,46 @@ +component extends="coldbox.system.ioc.config.Binder"{ + + /** + * Configure WireBox, that's it! + */ + function configure(){ + + // The WireBox configuration structure DSL + wireBox = { + // Scope registration, automatically register a wirebox injector instance on any CF scope + // By default it registeres itself on application scope + scopeRegistration = { + enabled = true, + scope = "application", // server, cluster, session, application + key = "wireBox" + }, + + // DSL Namespace registrations + customDSL = { + // namespace = "mapping name" + }, + + // Custom Storage Scopes + customScopes = { + // annotationName = "mapping name" + }, + + // Package scan locations + scanLocations = [], + + // Stop Recursions + stopRecursions = [], + + // Parent Injector to assign to the configured injector, this must be an object reference + parentInjector = "", + + // Register all event listeners here, they are created in the specified order + listeners = [ + // { class="", name="", properties={} } + ] + }; + + // Map Bindings below + } + +} \ No newline at end of file diff --git a/test-harness/handlers/Main.cfc b/test-harness/handlers/Main.cfc new file mode 100644 index 0000000..392cefa --- /dev/null +++ b/test-harness/handlers/Main.cfc @@ -0,0 +1,15 @@ +component{ + + property name="bcrypt" inject="@bcrypt"; + + function index( event, rc, prc ){ + return { + "hash" : bcrypt.hashPassword( rc.input ?: "test" ), + "salt" : bcrypt.generateSalt(), + "mixin-hash" : bcryptHash( rc.input ?: "test" ), + "mixin-salt" : bcryptSalt( 12 ), + "mixin-check" : bcryptCheck( "test", bcryptHash( rc.input ?: "test" ) ) + }; + } + +} \ No newline at end of file diff --git a/index.cfm b/test-harness/index.cfm similarity index 89% rename from index.cfm rename to test-harness/index.cfm index be3dc78..7331009 100644 --- a/index.cfm +++ b/test-harness/index.cfm @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + #testbox.init( directory=rootMapping & url.path ).run()# + +

Invalid incoming directory: #rootMapping & url.path#

+
+ + +
+ + + + + + + + + + + + + + + + + + + TestBox Browser + + + + + + + + + + + + + +
+
+
+ +
+ v#testbox.getVersion()# +
+ +
+
+
+
+
+ +

TestBox Test Browser:

+

+ Below is a listing of the files and folders starting from your root #rootPath#. You can click on individual tests in order to execute them + or click on the Run All button on your left and it will execute a directory runner from the visible folder. +

+ +
+ Contents: #executePath# + +

+
+ + + + + + + + ✚ #qResults.name#
+ + target="_blank"
>#qResults.name#
+ + target="_blank">#qResults.name#
+ + #qResults.name#
+ + +
+
+
+
+
+
+ + + +
\ No newline at end of file diff --git a/tests/runner.cfm b/test-harness/tests/runner.cfm similarity index 100% rename from tests/runner.cfm rename to test-harness/tests/runner.cfm diff --git a/test-harness/tests/specs/BCryptTests.cfc b/test-harness/tests/specs/BCryptTests.cfc new file mode 100644 index 0000000..b96f882 --- /dev/null +++ b/test-harness/tests/specs/BCryptTests.cfc @@ -0,0 +1,98 @@ +/** + * My BDD Test + */ +component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" { + + /*********************************** LIFE CYCLE Methods ***********************************/ + + // executes before all suites+specs in the run() method + function beforeAll(){ + super.beforeAll(); + + variables.passwordTestString = "F}766dVr7XzdEa2<>!%&^%$##@!)( * )/|\"; + } + + // executes after all suites+specs in the run() method + function afterAll(){ + super.afterAll(); + } + + /*********************************** BDD SUITES ***********************************/ + + function run( testResults, testBox ){ + // all your suites go here. + describe( "BCrypt Module", function(){ + beforeEach( function( currentSpec ){ + variables.bcrypt = getInstance( "bcrypt@bcrypt" ); + } ); + + it( "Hashes and salts a password", function(){ + // BCrypt salts the password each time before hashing it, resulting in a different hash value each time (the point of salting) + expect( variables.bcrypt.hashPassword( variables.passwordTestString ) ).notToBe( + variables.bcrypt.hashPassword( variables.passwordTestString ) + ); + } ); + + it( "Demonstrates the ability to verify password equality", function(){ + // this is a BCrypt hash of our password test string + var bCryptHashOfPasswordTest = "$2a$12$whxAYoS9Myu3dXokd/mlPOke6lJGlMA87kkCjK7v2bg9Pdcw/So1q"; + + expect( + variables.bcrypt.checkPassword( + variables.passwordTestString, + bCryptHashOfPasswordTest + ) + ).toBeTrue(); + } ); + + it( "Will produce a fixed length string", function(){ + expect( len( variables.bcrypt.hashPassword( variables.passwordTestString ) ) ).toBe( + 60, + "Database storage expects a BCrypt hash to be a 60 character string" + ); + } ); + + it( "Performs at a consistent high/low speed threshold", function(){ + var start = getTickCount(); + variables.bcrypt.hashPassword( variables.passwordTestString ); + var end = getTickCount(); + var executionDuration = end - start; + debug( "#executionDuration# milliseconds" ); + + // this might fail sometimes locally, we should judge it on a server though (aka the CI server) + expect( executionDuration ).toBeLT( + 750, + "#executionDuration#ms - Password hashing must be faster than .75 seconds or it will impact the user experience on logging in too much." + ); + expect( executionDuration ).toBeGT( + 100, + "#executionDuration#ms -Password hashing must be slower than .1 seconds or it will be too easy to brute force in an offline attack" + ); + } ); + + it( "is singleton safe and will not leak with a reasonable number of concurrent iterations", function(){ + for ( var i = 1; i <= 30; i++ ) { + var start = getTickCount(); + + // we're using a new instance each time in this loop to ensure singleton scope + getInstance( "bcrypt@bcrypt" ).hashPassword( variables.passwordTestString & i ); + + var end = getTickCount(); + + var executionDuration = end - start; + + // this might fail sometimes locally, we should judge it on a server though (aka the CI server) + expect( executionDuration ).toBeLT( + 750, + "#executionDuration#ms - Password hashing must be faster than .75 seconds or it will impact the user experience on logging in too much." + ); + expect( executionDuration ).toBeGT( + 100, + "#executionDuration#ms - Password hashing must be slower than .1 seconds or it will be too easy to brute force in an offline attack" + ); + } + } ); + } ); + } + +} diff --git a/tests/Application.cfc b/tests/Application.cfc deleted file mode 100644 index 7685275..0000000 --- a/tests/Application.cfc +++ /dev/null @@ -1,28 +0,0 @@ -/** -* Copyright Since 2005 Ortus Solutions, Corp -* www.coldbox.org | www.luismajano.com | www.ortussolutions.com | www.gocontentbox.org -************************************************************************************** -*/ -component{ - this.name = "A TestBox Runner Suite " & hash( getCurrentTemplatePath() ); - // any other application.cfc stuff goes below: - this.sessionManagement = true; - // Turn on/off white space managemetn - this.whiteSpaceManagement = "smart"; - - // any mappings go here, we create one that points to the root called test. - this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() ); - rootPath = REReplaceNoCase( this.mappings[ "/tests" ], "tests(\\|/)", "" ); - this.mappings[ "/root" ] = rootPath; - this.mappings[ "/coldbox" ] = rootPath & "/coldbox"; - this.mappings[ "/testbox" ] = rootPath & "/testbox"; - - this.mappings[ "/bcrypt" ] = rootPath & "/bcrypt"; - this.mappings[ "/cbjavaloader" ] = rootPath & "/cbjavaloader"; - - - // request start - public boolean function onRequestStart( String targetPage ){ - return true; - } -} \ No newline at end of file diff --git a/tests/specs/unit/BCryptTests.cfc b/tests/specs/unit/BCryptTests.cfc deleted file mode 100644 index 729b49a..0000000 --- a/tests/specs/unit/BCryptTests.cfc +++ /dev/null @@ -1,79 +0,0 @@ -/** -* My BDD Test -*/ -component extends="coldbox.system.testing.BaseTestCase" appMapping="/root"{ - -/*********************************** LIFE CYCLE Methods ***********************************/ - - // executes before all suites+specs in the run() method - function beforeAll(){ - super.beforeAll(); - } - - // executes after all suites+specs in the run() method - function afterAll(){ - super.afterAll(); - } - -/*********************************** BDD SUITES ***********************************/ - - function run( testResults, testBox ){ - // all your suites go here. - describe( "BCrypt Module", function(){ - - beforeEach(function( currentSpec ){ - BCrypt = getModel( "bcrypt@bcrypt" ); - }); - - it( "Hashes and salts a password", function(){ - // BCrypt salts the password each time before hashing it, resulting in a different hash value each time (the point of salting) - expect( BCrypt.hashPassword('test') ).notToBe( BCrypt.hashPassword('test') ); - }); - - it( "Demonstrates the ability to verify password equality", function(){ - // this is a BCrypt hash of "test" - var bCryptHashOfPasswordTest = '$2a$12$FE2J7ZLWaI2rSqejAu/84uLy7qlSufQsDsSE1lNNKyA05GG30gr8C'; - - expect( BCrypt.checkPassword( 'test', bCryptHashOfPasswordTest ) ).toBeTrue(); - }); - - it( "Will produce a fixed length string", function(){ - expect( len( BCrypt.hashPassword('test') ) ).toBe( 60, "Database storage expects a BCrypt hash to be a 60 character string" ); - }); - - it( "Performs at a consistent high/low speed threshold", function(){ - var start = getTickCount(); - BCrypt.hashPassword( 'test' ); - var end = getTickCount(); - var executionDuration = end - start; - debug( '#executionDuration# milliseconds' ); - - // this might fail sometimes locally, we should judge it on a server though (aka the CI server) - expect( executionDuration ).toBeLT( 750, "#executionDuration#ms - Password hashing must be faster than .75 seconds or it will impact the user experience on logging in too much." ); - expect( executionDuration ).toBeGT( 100, "#executionDuration#ms -Password hashing must be slower than .1 seconds or it will be too easy to brute force in an offline attack" ); - }); - - it( "is singleton safe and will not leak with a reasonable number of concurrent iterations", function(){ - - for( var i = 1; i <= 30; i++ ){ - - var start = getTickCount(); - - //we're using a new instance each time in this loop to ensure singleton scope - getModel( "bcrypt@bcrypt" ).hashPassword( 'test' & i ); - - var end = getTickCount(); - - var executionDuration = end - start; - - // this might fail sometimes locally, we should judge it on a server though (aka the CI server) - expect( executionDuration ).toBeLT( 750, "#executionDuration#ms - Password hashing must be faster than .75 seconds or it will impact the user experience on logging in too much." ); - expect( executionDuration ).toBeGT( 100, "#executionDuration#ms - Password hashing must be slower than .1 seconds or it will be too easy to brute force in an offline attack" ); - } - - }); - - }); - } - -} \ No newline at end of file diff --git a/tests/test-auto.properties b/tests/test-auto.properties deleted file mode 100644 index b14c63a..0000000 --- a/tests/test-auto.properties +++ /dev/null @@ -1 +0,0 @@ -url.runner=http://integration.stg.ortussolutions.com/codedepot/cbox-debugger/tests/runner.cfm? \ No newline at end of file diff --git a/tests/test.properties b/tests/test.properties deleted file mode 100644 index 6e00f38..0000000 --- a/tests/test.properties +++ /dev/null @@ -1,5 +0,0 @@ -url.runners.lucee@4.5=http://127.0.0.1:50680/tests/runner.cfm? -url.runners.lucee@5=http://127.0.0.1:50685/tests/runner.cfm? -url.runners.adobe@10=http://127.0.0.1:49610/tests/runner.cfm? -url.runners.adobe@11=http://127.0.0.1:49611/tests/runner.cfm? -url.runners.adobe@2016=http://127.0.0.1:49616/tests/runner.cfm? \ No newline at end of file diff --git a/tests/test.xml b/tests/test.xml deleted file mode 100644 index ea2608e..0000000 --- a/tests/test.xml +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Loading properties from environment: ${environment} - - - - - Loading base properties - - - - - - - - - - - - Tests ran at ${start.TODAY} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/views/.gitkeep b/views/.gitkeep deleted file mode 100644 index af18084..0000000 --- a/views/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -Reserved for views \ No newline at end of file diff --git a/views/main/index.cfm b/views/main/index.cfm deleted file mode 100644 index 74306e8..0000000 --- a/views/main/index.cfm +++ /dev/null @@ -1 +0,0 @@ -BCrypt Module Scaffold is Up and Running! \ No newline at end of file