├── .editorconfig ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE ├── OLD-README.md ├── README.md ├── UPGRADING.md ├── benchmark └── api-key-encryption.js ├── collection_each_example.js ├── docs-dev.sh ├── docs ├── JSDOC.md ├── develop.sh ├── jsdoc.json └── template │ ├── jsdoc.conf.json │ ├── publish.js │ ├── static │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ ├── scripts │ │ ├── docstrap.lib.js │ │ ├── fulltext-search-ui.js │ │ ├── fulltext-search.js │ │ ├── lunr.min.js │ │ ├── prettify │ │ │ ├── Apache-License-2.0.txt │ │ │ ├── jquery.min.js │ │ │ ├── lang-css.js │ │ │ └── prettify.js │ │ ├── sunlight.js │ │ └── toc.js │ └── styles │ │ ├── site.cosmo.css │ │ ├── sunlight.dark.css │ │ └── sunlight.default.css │ └── tmpl │ ├── augments.tmpl │ ├── container.tmpl │ ├── details.tmpl │ ├── example.tmpl │ ├── examples.tmpl │ ├── exceptions.tmpl │ ├── fires.tmpl │ ├── layout.tmpl │ ├── mainpage.tmpl │ ├── members.tmpl │ ├── method.tmpl │ ├── params.tmpl │ ├── properties.tmpl │ ├── quicksearch.tmpl │ ├── returns.tmpl │ ├── sections.tmpl │ ├── source.tmpl │ ├── tutorial.tmpl │ └── type.tmpl ├── lib ├── Client.js ├── authc │ ├── ApiKey.js │ ├── ApiKeyEncryptedOptions.js │ ├── ApiKeyLoader.js │ ├── AssertionAuthenticationResult.js │ ├── AuthRequestParser.js │ ├── BasicApiAuthenticator.js │ ├── BasicRequestAuthenticator.js │ ├── ClientCredentialGrantAuthenticator.js │ ├── OAuthBasicExchangeAuthenticator.js │ ├── OauthAccessTokenAuthenticator.js │ ├── RequestAuthenticator.js │ ├── Sauthc1RequestAuthenticator.js │ ├── StormpathAssertionAuthenticator.js │ └── index.js ├── cache │ ├── Cache.js │ ├── CacheEntry.js │ ├── CacheHandler.js │ ├── CacheManager.js │ ├── CacheStats.js │ ├── DisabledCache.js │ ├── MemcachedStore.js │ ├── MemoryStore.js │ ├── RedisStore.js │ └── index.js ├── config.yml ├── configLoader.js ├── ds │ ├── DataStore.js │ ├── NonceStore.js │ └── RequestExecutor.js ├── error │ ├── ApiAuthRequestError.js │ ├── ResourceError.js │ ├── invalid-href.js │ └── messages.js ├── index.js ├── jwt │ ├── jwt-authentication-result.js │ └── jwt-authenticator.js ├── oauth │ ├── authenticator.js │ ├── client-credentials.js │ ├── id-site-grant.js │ ├── password-grant.js │ ├── refresh-grant.js │ ├── scope-factory-authenticator.js │ ├── stormpath-access-token-authentication-result.js │ ├── stormpath-access-token-authenticator.js │ ├── stormpath-social.js │ └── stormpath-token.js ├── proxy │ └── ObjectCallProxy.js ├── resource │ ├── AccessToken.js │ ├── Account.js │ ├── AccountCreationPolicy.js │ ├── AccountLink.js │ ├── AccountLinkingPolicy.js │ ├── AccountStoreMapping.js │ ├── ApiKey.js │ ├── Application.js │ ├── ApplicationAccountStoreMapping.js │ ├── AuthenticationResult.js │ ├── Challenge.js │ ├── CollectionResource.js │ ├── CustomData.js │ ├── Directory.js │ ├── DirectoryChildResource.js │ ├── Factor.js │ ├── FactorInstantiator.js │ ├── Field.js │ ├── GoogleAuthenticatorFactor.js │ ├── Group.js │ ├── GroupMembership.js │ ├── IdSiteModel.js │ ├── InstanceResource.js │ ├── OAuthPolicy.js │ ├── Organization.js │ ├── OrganizationAccountStoreMapping.js │ ├── PasswordPolicy.js │ ├── PasswordResetToken.js │ ├── Phone.js │ ├── Provider.js │ ├── ProviderData.js │ ├── RefreshToken.js │ ├── Resource.js │ ├── ResourceFactory.js │ ├── SamlAttributeStatementMappingRules.js │ ├── SamlPolicy.js │ ├── SamlProvider.js │ ├── SamlServiceProvider.js │ ├── SamlServiceProviderMetadata.js │ ├── Schema.js │ ├── SmsFactor.js │ ├── SmtpServer.js │ ├── Strength.js │ ├── Tenant.js │ └── mixins │ │ └── SaveableMixin.js ├── saml │ └── SamlIdpUrlBuilder.js ├── stormpath.js ├── underscore.js └── utils.js ├── package.json ├── quickstart.js └── test ├── apikey_test.js ├── common.js ├── fixtures ├── account-token.js └── apiKey.properties ├── it ├── access-token-authenticator.js ├── account_it.js ├── api_auth_it.js ├── api_keys_it.js ├── application_it.js ├── assertion_authentication_result_it.js ├── client_credential_auth_it.js ├── client_it.js ├── datastore_it.js ├── directory_it.js ├── group_it.js ├── helpers.js ├── jwt_authenticator_it.js ├── oauth_authenticator_it.js ├── oauth_client_credentials_it.js ├── oauth_id_site_token_grant_it.js ├── oauth_password_grant_it.js ├── oauth_refresh_grant_it.js ├── oauth_stormpath_socal_it.js ├── oauth_stormpath_token_it.js ├── organization_it.js ├── password_policy_it.js ├── saml_idp_url_builder_it.js ├── stormpath_assertion_authenticator_it.js └── tenant_it.js ├── live ├── sp.cache.memcachedStore_live.js └── sp.cache.redisStore_live.js ├── sp.auth.unit_test.js ├── sp.authc.apiKeyLoader_test.js ├── sp.authc.apiKey_test.js ├── sp.authc.authRequestParser_test.js ├── sp.authc.requestAuthenticator_test.js ├── sp.cache.cacheHandler_test.js ├── sp.cache.cache_test.js ├── sp.cache.disabledCache_test.js ├── sp.cache.entry_test.js ├── sp.cache.manager_test.js ├── sp.cache.memoryStore_test.js ├── sp.cache.stats_test.js ├── sp.client_test.js ├── sp.config.configLoader_test.js ├── sp.ds.datastore_test.js ├── sp.ds.requestExecutor_test.js ├── sp.error.resourceError_test.js ├── sp.proxy.objectcallproxy_test.js ├── sp.resource.accountLink_test.js ├── sp.resource.accountLinkingPolicy_test.js ├── sp.resource.accountStoreMapping_test.js ├── sp.resource.account_test.js ├── sp.resource.apikey_test.js ├── sp.resource.application_test.js ├── sp.resource.authenticationResult_test.js ├── sp.resource.challenge_test.js ├── sp.resource.collectionResource_test.js ├── sp.resource.customData_test.js ├── sp.resource.directoryChildResource_test.js ├── sp.resource.directory_test.js ├── sp.resource.factorInstantiator_test.js ├── sp.resource.factor_test.js ├── sp.resource.field_test.js ├── sp.resource.googleAuthenticatorFactor_test.js ├── sp.resource.groupMembership_test.js ├── sp.resource.group_test.js ├── sp.resource.instanceResource_test.js ├── sp.resource.organization_test.js ├── sp.resource.phone_test.js ├── sp.resource.resourceFactory_test.js ├── sp.resource.resource_test.js ├── sp.resource.schema_test.js ├── sp.resource.smsFactor_test.js ├── sp.resource.tenant_test.js └── sp.utils_test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Indentation override for all JS under lib directory 7 | [**.js] 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | indent_style = space 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_size = 2 14 | 15 | [**.yml] 16 | indent_style = space 17 | indent_size = 2 18 | end_of_line = lf 19 | charset = utf-8 20 | trim_trailing_whitespace = true 21 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /coverage 3 | apidocs 4 | npm-debug.log 5 | *.ipr 6 | *.iws 7 | *.iml 8 | .idea 9 | .DS_Store 10 | .coveralls.yml 11 | .env 12 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "globals": { 15 | /* MOCHA */ 16 | "describe": false, 17 | "it": false, 18 | "before": false, 19 | "beforeEach": false, 20 | "after": false, 21 | "afterEach": false 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .coveralls.yml 2 | .env 3 | apidocs 4 | docs 5 | samples 6 | test 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.12' 5 | - 'iojs' 6 | - '4' 7 | - '6' 8 | sudo: false 9 | script: travis_retry npm test 10 | env: 11 | global: 12 | - secure: axSfLtwNV9cKG+Y7UslwPddxyPbvbEwbyWDUt9ONRkFIpNAbtuRvjSSl2tAmJ9m9vFDV0I63shYaCkwz6gFiJDGRlYjWHqgBknm8jOBLYIk68mrSHzQZLdoAqoEhAe+UCJSvoQGkLeuPuVw5/lVWZ6Uv6dzSsU/8dVlsp23G9Hc= 13 | - secure: RVyH3nRU3yjeVUCMkOYy7KuRG0bFHMANbQsEbxuZ2ad/KMYNNDJ/AfyO3Q5ojAj/uX59NLYyzyFODhXikEaFsIvgCHbGLkgBYSNIVUIdaS509KMOUwcN6ZDDUpVS21E2rEJwY7/BGenJKZJ1N3NLf6yqciS2KYoo7TVdnTaNUh8= 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | We love pull requests! 4 | 5 | Here are a few things you'll need to know if you want to make a contribution to 6 | this library. 7 | 8 | ### Submitting Pull Requests 9 | 10 | Pull requests are welcome, and will be reviewed as soon as possible. Please 11 | ensure that the tests are passing and that new features have test coverage. When 12 | you submit the PR the tests will fail on Travis, even if they are working locally. 13 | This is because we use encrypted environment variables on Travis, and this will 14 | fail for PRs from forks. This is okay, we will pull down your branch and run 15 | the tests locally for confirmation. 16 | 17 | ### Documentation 18 | 19 | This library uses JsDoc for documentation. To build the docs: 20 | 21 | ``` 22 | npm run docs 23 | ``` 24 | 25 | The output will be placed in `./apidocs`. If you are going to do a lot of work 26 | on the documentation, I suggest the development script, it will rebuild the 27 | files when you make changes, and open a web server with the documentation: 28 | 29 | ``` 30 | ./docs/develop.sh 31 | ``` 32 | 33 | If you need to modify the theme or template, you can find that in `docs/template`. 34 | This template was cloned from the [Docstrap][] project. 35 | 36 | [Docstrap]: https://github.com/docstrap/docstrap 37 | 38 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (grunt) { 4 | 5 | // Show elapsed time at the end 6 | require('time-grunt')(grunt); 7 | // Load all grunt tasks 8 | require('load-grunt-tasks')(grunt); 9 | 10 | // Project configuration. 11 | grunt.initConfig({ 12 | mochaTest: { 13 | test: { 14 | src: ['test/**/*_test.js'], 15 | options: { 16 | timeout: 30000 17 | } 18 | 19 | }, 20 | live: { 21 | src: ['test/**/*_live.js'] 22 | } 23 | }, 24 | mocha_istanbul: { 25 | test: { 26 | src: ['test/it/*_it.js', 'test/**/*_test.js'], 27 | options: { 28 | coverage: true, 29 | require: ['test/common.js'], 30 | timeout: 30000 31 | } 32 | }, 33 | coverage: { 34 | src: 'test', // the folder, not the files, 35 | options: { 36 | mask: '**/*_test.js', 37 | require: ['test/common.js'], 38 | timeout: 30000 39 | } 40 | }, 41 | it: { 42 | src: 'test', // the folder, not the files, 43 | options: { 44 | mask: '**/*_it.js', 45 | require: ['test/common.js'], 46 | timeout: 30000 47 | } 48 | } 49 | }, 50 | jshint: { 51 | options: { 52 | jshintrc: '.jshintrc', 53 | reporter: require('jshint-stylish') 54 | }, 55 | gruntfile: { 56 | src: 'Gruntfile.js' 57 | }, 58 | lib: { 59 | src: ['lib/**/*.js'] 60 | }, 61 | test: { 62 | src: ['test/**/*.js'] 63 | } 64 | }, 65 | watch: { 66 | gruntfile: { 67 | files: '<%= jshint.gruntfile.src %>', 68 | tasks: ['jshint:gruntfile'] 69 | }, 70 | lib: { 71 | files: '<%= jshint.lib.src %>', 72 | tasks: ['jshint:lib', 'mochaTest'] 73 | }, 74 | test: { 75 | files: '<%= jshint.test.src %>', 76 | tasks: ['jshint:test', 'mochaTest'] 77 | } 78 | } 79 | }); 80 | 81 | grunt.loadNpmTasks('grunt-contrib-jshint'); 82 | grunt.loadNpmTasks('grunt-contrib-watch'); 83 | grunt.loadNpmTasks('grunt-mocha-istanbul'); 84 | grunt.loadNpmTasks('grunt-mocha-test'); 85 | 86 | // Default task. 87 | grunt.registerTask('test', ['mochaTest:test']); 88 | grunt.registerTask('it', ['jshint','mocha_istanbul:it']); 89 | grunt.registerTask('live', ['mochaTest:live']); 90 | grunt.registerTask('coverage', ['mocha_istanbul:coverage']); 91 | grunt.registerTask('default', ['jshint', 'mocha_istanbul:test']); 92 | 93 | // Once our coverage reports have been generated, fire off coverage reports to 94 | // coveralls.io so our coverage is made public. 95 | grunt.event.on('coverage', function(lcov, done) { 96 | require('coveralls').handleInput(lcov, function(err) { 97 | done(err); 98 | }); 99 | }); 100 | }; 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stormpath is Joining Okta 2 | 3 | We are incredibly excited to announce that [Stormpath is joining forces with Okta](https://stormpath.com/blog/stormpaths-new-path?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement). Please visit [the Migration FAQs](https://stormpath.com/oktaplusstormpath?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement) for a detailed look at what this means for Stormpath users. 4 | 5 | We're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com). 6 | 7 | ## What does this mean for developers who are using this library? 8 | 9 | This library is not being patched to work with Okta. If you are using this library, you should consider using the new [Okta Node SDK][] or manually integrating with an HTTP client. Please see the [Okta API reference][] for more information about the API. 10 | 11 | If you are using [Express-Stormpath][], that library is depending on the [okta branch] in this library. That branch is being maintained for [Express-Stormpath][] only. Using this branch directly is not supported. 12 | 13 | ## README 14 | 15 | If you are actively using this library, you can find the readme in [OLD-README.md](OLD-README.md). 16 | It is not possible to register for new Stormpath tenants at this time, so you must 17 | already have a Stormpath tenant if you wish to use this library during the migration 18 | period. 19 | 20 | [Express-Stormpath]: https://github.com/stormpath/express-stormpath 21 | [okta branch]: https://github.com/stormpath/stormpath-sdk-node/tree/okta 22 | [Okta API reference]: https://okta.github.io/docs/api/resources/apps.html 23 | [Okta Node SDK]: https://github.com/okta/okta-sdk-nodejs -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | ### Version 0.20.0 -> Version 0.20.1 4 | 5 | No changes needed 6 | 7 | ### Version 0.19.2 -> Version 0.20.0 8 | 9 | The authenticator class `JwtAuthenticator` has been deprecated, please use `StormpathAccessTokenAuthenticator` instead. 10 | 11 | ### Version 0.19.1 -> Version 0.19.2 12 | 13 | No changes needed 14 | 15 | ### Version 0.19.0 -> Version 0.19.1 16 | 17 | No changes needed 18 | 19 | ### Version 0.18.5 -> Version 0.19.0 20 | 21 | No changes needed 22 | 23 | ### Version 0.18.4 -> Version 0.18.5 24 | 25 | No changes needed 26 | 27 | ### Version 0.18.3 -> Version 0.18.4 28 | 29 | No changes needed 30 | 31 | ### Version 0.18.2 -> Version 0.18.3 32 | 33 | No changes needed 34 | 35 | ### Version 0.18.1 -> Version 0.18.2 36 | 37 | No changes needed 38 | 39 | ### Version 0.18.0 -> Version 0.18.1 40 | 41 | No changes needed 42 | 43 | ### Version 0.17.5 -> Version 0.18.0 44 | 45 | No changes needed 46 | 47 | ### Version 0.17.4 -> Version 0.17.5 48 | 49 | No changes needed 50 | 51 | ### Version 0.17.3 -> Version 0.17.4 52 | 53 | No changes needed 54 | 55 | ### Version 0.17.2 -> Version 0.17.3 56 | 57 | No changes needed 58 | 59 | ### Version 0.17.1 -> Version 0.17.2 60 | 61 | No changes needed 62 | 63 | ### Version 0.17.0 -> Version 0.17.1 64 | 65 | No changes needed 66 | 67 | ### Version 0.16.0 -> Version 0.17.0 68 | 69 | * `OAuthIdSiteTokenGrantAuthenticator` has been deprecated. Please use the 70 | new `OAuthStormpathTokenAuthenticator` instead. 71 | 72 | ### Version 0.15.5 -> Version 0.16.0 73 | 74 | No changes needed 75 | 76 | ### Version 0.15.4 -> Version 0.15.5 77 | 78 | No changes needed 79 | 80 | ### Version 0.15.3 -> Version 0.15.4 81 | 82 | No changes needed 83 | 84 | ### Version 0.15.2 -> Version 0.15.3 85 | 86 | No changes needed 87 | 88 | ### Version 0.15.1 -> Version 0.15.2 89 | 90 | No changes needed 91 | 92 | ### Version 0.15.0 -> Version 0.15.1 93 | 94 | No changes needed 95 | 96 | ### Version 0.14.0 -> Version 0.15.0 97 | 98 | If you were relying on this module to enrich your client configuration with 99 | a Stormpath application and default account store, you will need to move 100 | that strategy, `EnrichClientFromRemoteConfigStrategy()`, into your application's 101 | client configuration strategy list (it is no longer done by this library). 102 | 103 | ### Version 0.13.5 -> Version 0.14.0 104 | 105 | No changes needed 106 | 107 | ### Version 0.13.4 -> Version 0.13.5 108 | 109 | No changes needed 110 | 111 | ### Version 0.13.3 -> Version 0.13.4 112 | 113 | No changes needed 114 | 115 | ### Version 0.13.2 -> Version 0.13.3 116 | 117 | No changes needed 118 | 119 | ### Version 0.13.1 -> Version 0.13.2 120 | 121 | No changes needed 122 | 123 | ### Version 0.13.0 -> Version 0.13.1 124 | 125 | No changes needed 126 | -------------------------------------------------------------------------------- /benchmark/api-key-encryption.js: -------------------------------------------------------------------------------- 1 | var stormpath = require('../'); 2 | var uuid = require('uuid'); 3 | var base64 = require('../lib/utils').base64; 4 | var Benchmark = require('benchmark'); 5 | var async = require('async'); 6 | 7 | function invoke(app,apiKey,cb){ 8 | app.authenticateApiRequest({ 9 | request:{ 10 | method: 'GET', 11 | url: '/', 12 | headers:{ 13 | 'authorization': 'Basic '+ base64.encode(apiKey.id+':'+apiKey.secret) 14 | } 15 | } 16 | },cb); 17 | } 18 | 19 | function addTestRunner(apiKey,test,suite,done){ 20 | var client = new stormpath.Client({ 21 | apiKey: new stormpath.ApiKey( 22 | process.env['STORMPATH_API_KEY_ID'], 23 | process.env['STORMPATH_API_KEY_SECRET'] 24 | ), 25 | apiKeyEncryptionOptions: test.apiKeyEncryptionOptions 26 | }); 27 | 28 | client.getApplication(process.env['STORMPATH_APP_HREF'],function(err,app) { 29 | 30 | if(err){ throw err; } 31 | 32 | // Make an initial invocation to warm the cache 33 | 34 | invoke(app,apiKey,function(err){ 35 | if(err){ throw err; } 36 | suite.add(test.title, { 37 | defer: true, 38 | fn: function(deferred){ 39 | invoke(app,apiKey,function(err){ 40 | if(err){ 41 | throw err; 42 | }else{ 43 | deferred.resolve(); 44 | } 45 | }); 46 | } 47 | }); 48 | done(); 49 | }); 50 | }); 51 | } 52 | 53 | 54 | 55 | var tests = [ 56 | { 57 | title: 'With encryption DISABLED (encryptSecret: false) ', 58 | apiKeyEncryptionOptions: { 59 | encryptSecret: false 60 | } 61 | }, 62 | { 63 | title: 'With encryption ENABLED (default options) ' 64 | }, 65 | { 66 | title: 'With light encryption (128 key size, iterations) ', 67 | apiKeyEncryptionOptions: { 68 | encryptionKeySize: 128, 69 | encryptionKeyIterations: 128 70 | } 71 | }, 72 | { 73 | title: 'With medium encryption (128 key size, iterations)', 74 | apiKeyEncryptionOptions: { 75 | encryptionKeySize: 256, 76 | encryptionKeyIterations: 512 77 | } 78 | } 79 | ]; 80 | 81 | 82 | console.log('Construct Client'); 83 | 84 | // First client is used to create our dummy account 85 | 86 | var client1 = new stormpath.Client({ 87 | apiKey: new stormpath.ApiKey( 88 | process.env['STORMPATH_API_KEY_ID'], 89 | process.env['STORMPATH_API_KEY_SECRET'] 90 | ) 91 | }); 92 | 93 | 94 | var account; 95 | 96 | 97 | var suite = new Benchmark.Suite('api auth') 98 | .on('start', function() { 99 | console.log('Begin benchmarks'); 100 | }) 101 | .on('cycle', function(event) { 102 | console.log(String(event.target)); 103 | }) 104 | .on('complete', function() { 105 | console.log('\nFastest is: ' + this.filter('fastest').pluck('name')+'\n'); 106 | account.delete(function(err){ 107 | if(err){ throw err; } 108 | console.log('Deleted account'); 109 | }); 110 | }); 111 | 112 | 113 | client1.getApplication(process.env['STORMPATH_APP_HREF'],function(err,app1) { 114 | 115 | if(err){ throw err; } 116 | 117 | console.log('Create test account'); 118 | 119 | app1.createAccount({ 120 | email: uuid()+'@stormpath.com', 121 | password: uuid() + 'ABC1', 122 | givenName: uuid(), 123 | surname: uuid() 124 | },function(err,result){ 125 | 126 | if(err){ throw err; } 127 | 128 | account = result; 129 | 130 | account.createApiKey(function(err,apiKey){ 131 | 132 | if(err){ throw err; } 133 | 134 | async.parallel(tests.map(function(test){ 135 | return addTestRunner.bind(null,apiKey,test,suite); 136 | }),function(err){ 137 | if(err){ throw err; } 138 | suite.run({async:true}); 139 | }); 140 | 141 | }); 142 | }); 143 | }); 144 | -------------------------------------------------------------------------------- /docs-dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm run docs 4 | ./node_modules/httpster/bin/httpster -d ./apidocs & 5 | open http://localhost:3333 6 | ./node_modules/nodemon/bin/nodemon.js -w ./docs/JSDOC.md -w lib/ -x "npm run docs" -e js -------------------------------------------------------------------------------- /docs/develop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm run docs 4 | ./node_modules/httpster/bin/httpster -d ./apidocs & 5 | open http://localhost:3333 6 | ./node_modules/nodemon/bin/nodemon.js -w JSDOC.md -w lib/ -x "npm run docs" -e js -------------------------------------------------------------------------------- /docs/jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "plugins/markdown" 4 | ], 5 | "templates": { 6 | "linenums": true, 7 | "outputSourceFiles": false, 8 | "outputSourcePath": true, 9 | "systemName": "Stormpath Node SDK", 10 | "theme": "cosmo" 11 | }, 12 | "opts": { 13 | "template": "./docs/template" 14 | } 15 | } -------------------------------------------------------------------------------- /docs/template/jsdoc.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "plugins": ["plugins/markdown"], 6 | "templates": { 7 | "logoFile": "", 8 | "cleverLinks": false, 9 | "monospaceLinks": false, 10 | "dateFormat": "ddd MMM Do YYYY", 11 | "outputSourceFiles": true, 12 | "outputSourcePath": true, 13 | "systemName": "DocStrap", 14 | "footer": "", 15 | "copyright": "DocStrap Copyright © 2012-2015 The contributors to the JSDoc3 and DocStrap projects.", 16 | "navType": "vertical", 17 | "theme": "cosmo", 18 | "linenums": true, 19 | "collapseSymbols": false, 20 | "inverseNav": true, 21 | "protocol": "html://", 22 | "methodHeadingReturns": false 23 | }, 24 | "markdown": { 25 | "parser": "gfm", 26 | "hardwrap": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/template/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormpath/stormpath-sdk-node/da1d653d02a276296a0fc2616689c852d2c0db90/docs/template/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/template/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormpath/stormpath-sdk-node/da1d653d02a276296a0fc2616689c852d2c0db90/docs/template/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/template/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormpath/stormpath-sdk-node/da1d653d02a276296a0fc2616689c852d2c0db90/docs/template/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/template/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormpath/stormpath-sdk-node/da1d653d02a276296a0fc2616689c852d2c0db90/docs/template/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/template/static/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormpath/stormpath-sdk-node/da1d653d02a276296a0fc2616689c852d2c0db90/docs/template/static/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /docs/template/static/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stormpath/stormpath-sdk-node/da1d653d02a276296a0fc2616689c852d2c0db90/docs/template/static/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /docs/template/static/scripts/fulltext-search-ui.js: -------------------------------------------------------------------------------- 1 | window.SearcherDisplay = (function($) { 2 | /** 3 | * This class provides support for displaying quick search text results to users. 4 | */ 5 | function SearcherDisplay() { } 6 | 7 | SearcherDisplay.prototype.init = function() { 8 | this._displayQuickSearch(); 9 | }; 10 | 11 | /** 12 | * This method creates the quick text search entry in navigation menu and wires all required events. 13 | */ 14 | SearcherDisplay.prototype._displayQuickSearch = function() { 15 | var quickSearch = $(document.createElement("iframe")), 16 | body = $("body"), 17 | self = this; 18 | 19 | quickSearch.attr("src", "quicksearch.html"); 20 | quickSearch.css("width", "0px"); 21 | quickSearch.css("height", "0px"); 22 | 23 | body.append(quickSearch); 24 | 25 | $(window).on("message", function(msg) { 26 | var msgData = msg.originalEvent.data; 27 | 28 | if (msgData.msgid != "docstrap.quicksearch.done") { 29 | return; 30 | } 31 | 32 | var results = msgData.results || []; 33 | 34 | self._displaySearchResults(results); 35 | }); 36 | 37 | function startSearch() { 38 | var searchTerms = $('#search-input').prop("value"); 39 | if (searchTerms) { 40 | quickSearch[0].contentWindow.postMessage({ 41 | "searchTerms": searchTerms, 42 | "msgid": "docstrap.quicksearch.start" 43 | }, "*"); 44 | } 45 | } 46 | 47 | $('#search-input').on('keyup', function(evt) { 48 | if (evt.keyCode != 13) { 49 | return; 50 | } 51 | startSearch(); 52 | return false; 53 | }); 54 | $('#search-submit').on('click', function() { 55 | startSearch(); 56 | return false; 57 | }); 58 | }; 59 | 60 | /** 61 | * This method displays the quick text search results in a modal dialog. 62 | */ 63 | SearcherDisplay.prototype._displaySearchResults = function(results) { 64 | var resultsHolder = $($("#searchResults").find(".modal-body")), 65 | fragment = document.createDocumentFragment(), 66 | resultsList = document.createElement("ul"); 67 | 68 | resultsHolder.empty(); 69 | 70 | for (var idx = 0; idx < results.length; idx++) { 71 | var result = results[idx], 72 | item = document.createElement("li"), 73 | link = document.createElement("a"); 74 | 75 | link.href = result.id; 76 | link.innerHTML = result.title; 77 | 78 | item.appendChild(link) 79 | resultsList.appendChild(item); 80 | } 81 | 82 | fragment.appendChild(resultsList); 83 | resultsHolder.append(fragment); 84 | 85 | $("#searchResults").modal({"show": true}); 86 | }; 87 | 88 | return new SearcherDisplay(); 89 | })($); 90 | -------------------------------------------------------------------------------- /docs/template/static/scripts/fulltext-search.js: -------------------------------------------------------------------------------- 1 | window.Searcher = (function() { 2 | function Searcher() { 3 | this._index = lunr(function () { 4 | this.field('title', {boost: 10}) 5 | this.field('body') 6 | this.ref('id') 7 | }) ; 8 | 9 | this._indexContent = undefined; 10 | } 11 | 12 | Searcher.prototype.init = function() { 13 | var self = this; 14 | 15 | $("script[type='text/x-docstrap-searchdb']").each(function(idx, item) { 16 | self._indexContent = JSON.parse(item.innerHTML); 17 | 18 | for (var entryId in self._indexContent) { 19 | self._index.add(self._indexContent[entryId]); 20 | } 21 | }); 22 | }; 23 | 24 | Searcher.prototype.search = function(searchTerm) { 25 | var results = [], 26 | searchResults = this._index.search(searchTerm); 27 | 28 | for (var idx = 0; idx < searchResults.length; idx++) { 29 | results.push(this._indexContent[searchResults[idx].ref]) 30 | } 31 | 32 | return results; 33 | }; 34 | 35 | return new Searcher(); 36 | })(); -------------------------------------------------------------------------------- /docs/template/static/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([ 2 | ["pln", /^[\t\n\f\r ]+/, null, " \t\r\n "] 3 | ], [ 4 | ["str", /^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/, null], 5 | ["str", /^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/, null], 6 | ["lang-css-str", /^url\(([^"')]*)\)/i], 7 | ["kwd", /^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i, null], 8 | ["lang-css-kw", /^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i], 9 | ["com", /^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//], 10 | ["com", /^(?:<\!--|--\>)/], 11 | ["lit", /^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i], 12 | ["lit", /^#[\da-f]{3,6}/i], 13 | ["pln", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i], 14 | ["pun", /^[^\s\w"']+/] 15 | ]), ["css"]); 16 | PR.registerLangHandler(PR.createSimpleLexer([], [ 17 | ["kwd", /^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i] 18 | ]), ["css-kw"]); 19 | PR.registerLangHandler(PR.createSimpleLexer([], [ 20 | ["str", /^[^"')]+/] 21 | ]), ["css-str"]); -------------------------------------------------------------------------------- /docs/template/tmpl/augments.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /docs/template/tmpl/example.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
3 | -------------------------------------------------------------------------------- /docs/template/tmpl/examples.tmpl: -------------------------------------------------------------------------------- 1 | 6 |

7 | 8 |
9 | 12 | -------------------------------------------------------------------------------- /docs/template/tmpl/exceptions.tmpl: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 |
8 | 9 |
10 |
11 |
12 |
13 |
14 | Type 15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | -------------------------------------------------------------------------------- /docs/template/tmpl/fires.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
  • 3 | 4 |
  • -------------------------------------------------------------------------------- /docs/template/tmpl/mainpage.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 |

    9 | 10 | 11 | 12 |
    13 |
    14 |
    15 | 16 | -------------------------------------------------------------------------------- /docs/template/tmpl/members.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
    6 |
    7 |

    8 | 9 | 10 |

    11 | 12 |
    13 |
    14 | 15 |
    16 | 17 |
    18 | 19 | 20 | 21 |
    Type:
    22 | 27 | 28 | 29 | 30 | 31 | 32 |
    Example 1? 's':'' ?>
    33 | 34 | 35 |
    36 | -------------------------------------------------------------------------------- /docs/template/tmpl/method.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
    6 |
    7 |

    8 | 9 | 10 |

    11 | 12 |
    13 |
    14 | 15 | 16 |
    17 | 18 |
    19 | 20 | 21 | 22 |
    Extends:
    23 | 24 | 25 | 26 | 27 |
    Type:
    28 | 29 | 30 | 31 |
    This:
    32 | 33 | 34 | 35 |
    Parameters:
    36 | 37 | 38 | 39 | 40 | 41 | 42 |
    Requires:
    43 | 46 | 47 | 48 | 49 |
    Fires:
    50 | 53 | 54 | 55 | 56 |
    Listens to Events:
    57 | 60 | 61 | 62 | 63 |
    Listeners of This Event:
    64 | 67 | 68 | 69 | 70 |
    Throws:
    71 | 1) { ?> 77 | 78 | 80 | 81 | 82 |
    Returns:
    83 | 1) { ?> 89 | 90 | 92 | 93 | 94 |
    Example 1? 's':'' ?>
    95 | 96 | 97 |
    98 | -------------------------------------------------------------------------------- /docs/template/tmpl/params.tmpl: -------------------------------------------------------------------------------- 1 | 41 |
    42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 80 | 81 | 82 | 95 | 96 | 97 | 98 | 103 | 104 | 105 | 109 | 110 | 111 | 112 | 113 |
    NameTypeArgumentDefaultDescription
    76 | 77 | 78 | 79 | 83 | 84 | <optional>
    85 | 86 | 87 | 88 | <nullable>
    89 | 90 | 91 | 92 | <repeatable>
    93 | 94 |
    99 | 100 | 101 | 102 | 106 |
    Properties
    107 | 108 |
    114 |
    -------------------------------------------------------------------------------- /docs/template/tmpl/properties.tmpl: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 79 | 80 | 81 | 90 | 91 | 92 | 93 | 98 | 99 | 100 | 103 | 104 | 105 | 106 | 107 |
    NameTypeArgumentDefaultDescription
    75 | 76 | 77 | 78 | 82 | 83 | <optional>
    84 | 85 | 86 | 87 | <nullable>
    88 | 89 |
    94 | 95 | 96 | 97 | 101 |
    Properties
    102 |
    108 | -------------------------------------------------------------------------------- /docs/template/tmpl/quicksearch.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/template/tmpl/returns.tmpl: -------------------------------------------------------------------------------- 1 | 5 |
    6 | 7 |
    8 | 9 | 10 | 11 |
    12 |
    13 | Type 14 |
    15 |
    16 | 17 |
    18 |
    19 | 20 | -------------------------------------------------------------------------------- /docs/template/tmpl/sections.tmpl: -------------------------------------------------------------------------------- 1 | 2 |
    3 | 6 |
    7 | -------------------------------------------------------------------------------- /docs/template/tmpl/source.tmpl: -------------------------------------------------------------------------------- 1 | 4 |
    5 |
    6 |
    8 |
    9 |
    10 | -------------------------------------------------------------------------------- /docs/template/tmpl/tutorial.tmpl: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 | 0) { ?> 5 |
      8 |
    • 9 |
    10 | 11 | 12 |

    13 |
    14 | 15 |
    16 | 17 |
    18 | 19 |
    20 | -------------------------------------------------------------------------------- /docs/template/tmpl/type.tmpl: -------------------------------------------------------------------------------- 1 | 5 | 6 | | 7 | 8 | -------------------------------------------------------------------------------- /lib/authc/ApiKey.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | function ApiKey(id, secret) { 6 | this.id = id; 7 | this.secret = secret; 8 | } 9 | utils.inherits(ApiKey, Object); 10 | 11 | ApiKey.prototype.toString = function apiKeyToString() { 12 | return 'id: ' + this.id + ', secret: '; 13 | }; 14 | 15 | module.exports = ApiKey; 16 | 17 | -------------------------------------------------------------------------------- /lib/authc/ApiKeyEncryptedOptions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var uuid = require('uuid'); 4 | 5 | var utils = require('../utils'); 6 | 7 | function ApiKeyEncryptedOptions(options) { 8 | options = typeof options === 'object' ? options : {}; 9 | this.expand = "account"; 10 | 11 | if(options.id){ 12 | this.id = options.id; 13 | } 14 | 15 | if(options.encryptSecret !== false){ 16 | this.encryptSecret = true; 17 | this.encryptionKeySize = options.encryptionKeySize || 256; 18 | this.encryptionKeyIterations = options.encryptionKeyIterations || 1024; 19 | var salt = new Buffer(uuid().substr(0,16),'base64').toString('base64'); 20 | this.encryptionKeySalt = utils.base64.urlEncode(salt); 21 | } 22 | } 23 | utils.inherits(ApiKeyEncryptedOptions, Object); 24 | 25 | module.exports = ApiKeyEncryptedOptions; 26 | -------------------------------------------------------------------------------- /lib/authc/ApiKeyLoader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var propsParser = require('properties-parser'); 4 | 5 | var _ = require('../underscore'); 6 | var ApiKey = require('./ApiKey'); 7 | 8 | var FILE_URL_PREFIX = 'file://'; 9 | var DEFAULT_ID_PROP_NAME = 'apiKey.id'; 10 | var DEFAULT_SECRET_PROP_NAME = 'apiKey.secret'; 11 | 12 | function loadFile(path, callback) { 13 | return propsParser.read(path, function(err, props) { 14 | if (err) { 15 | return callback(new Error("Unable to read properties file '" + path + "': " + err.message), null); 16 | } 17 | 18 | var apiKey = new ApiKey(props[DEFAULT_ID_PROP_NAME], props[DEFAULT_SECRET_PROP_NAME]); 19 | return callback(null, apiKey); 20 | }); 21 | } 22 | 23 | function loadApiKey(path, callback) { 24 | if (_(path).startsWith(FILE_URL_PREFIX)) { 25 | path = path.substring(FILE_URL_PREFIX.length); 26 | } 27 | loadFile(path, callback); 28 | } 29 | 30 | module.exports = loadApiKey; 31 | -------------------------------------------------------------------------------- /lib/authc/AssertionAuthenticationResult.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Account = require('../resource/Account'); 4 | 5 | /** 6 | * Encapsulates the authentication result from an instance of {@link StormpathAssertionAuthenticator}. 7 | * 8 | * This class should not be manually constructed. It should be obtained from one of these methods: 9 | * 10 | * - {@link StormpathAssertionAuthenticator#authenticate StormpathAssertionAuthenticator.authenticate()}. 11 | * 12 | * @class 13 | */ 14 | function AssertionAuthenticationResult(dataStore, data) { 15 | Object.defineProperty(this, 'dataStore', { 16 | enumerable:false, 17 | value: dataStore 18 | }); 19 | 20 | // Copy data properties. 21 | if (data) { 22 | for (var key in data) { 23 | if (key in this) { 24 | continue; 25 | } 26 | this[key] = data[key]; 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * @function 33 | * 34 | * @description Get the account resource of the account that has authenticated. 35 | * 36 | * @param {Function} callback 37 | * 38 | * The callback to call with the parameters (err, {@link Account}). 39 | */ 40 | AssertionAuthenticationResult.prototype.getAccount = function getAccount(callback) { 41 | if (!this.account || !this.account.href) { 42 | return callback(new Error('Unable to get account. Account HREF not specified.')); 43 | } 44 | 45 | this.dataStore.getResource(this.account.href, Account, callback); 46 | }; 47 | 48 | module.exports = AssertionAuthenticationResult; 49 | -------------------------------------------------------------------------------- /lib/authc/AuthRequestParser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var url = require('url'); 4 | 5 | var ApiAuthRequestError = require('../error/ApiAuthRequestError'); 6 | 7 | function AuthRequestParser(request,locationsToSearch){ 8 | 9 | if(typeof request !=='object'){ 10 | throw new ApiAuthRequestError({userMessage: 'request must be an object'}); 11 | } 12 | if(typeof request.url !== 'string'){ 13 | throw new ApiAuthRequestError({userMessage: 'request must have a url string'}); 14 | } 15 | if(typeof request.headers !== 'object'){ 16 | throw new ApiAuthRequestError({userMessage: 'request must have a headers object'}); 17 | } 18 | if(typeof request.method !== 'string'){ 19 | throw new ApiAuthRequestError({userMessage: 'request must have a method property'}); 20 | } 21 | if(typeof locationsToSearch !== 'object') { 22 | throw new ApiAuthRequestError({userMessage: 'locationsToSearch must be an array'}); 23 | } 24 | 25 | var req = request; 26 | var searchBody = locationsToSearch.indexOf('body') > -1; 27 | var searchHeader = locationsToSearch.indexOf('header') > -1; 28 | var searchUrl = locationsToSearch.indexOf('url') > -1; 29 | 30 | var urlParams = url.parse(req.url,true).query; 31 | 32 | this.body = (searchBody && (typeof req.body === 'object') && (req.body !== null)) ? req.body : {}; 33 | this.headers = searchHeader ? req.headers : {}; 34 | 35 | this.grantType = this.body.grant_type || urlParams.grant_type || ''; 36 | this.authorizationValue = searchHeader ? 37 | (this.headers['authorization'] || this.headers['Authorization'] || '') : ''; 38 | this.accessToken = searchUrl ? 39 | (urlParams.access_token || this.body.access_token || this.authorizationValue) : 40 | (this.body.access_token || this.authorizationValue); 41 | 42 | this.requestedScope = (this.body.scope || urlParams.scope || '').split(' '); 43 | } 44 | 45 | module.exports = AuthRequestParser; 46 | -------------------------------------------------------------------------------- /lib/authc/BasicApiAuthenticator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ApiAuthRequestError = require('../error/ApiAuthRequestError'); 4 | var AuthenticationResult = require('../resource/AuthenticationResult'); 5 | 6 | function BasicApiAuthenticator(application,authHeaderValue, ttl){ 7 | var parts = new Buffer(authHeaderValue.replace(/Basic /i,''),'base64').toString('utf8').split(':'); 8 | 9 | if(parts.length !== 2){ 10 | return new ApiAuthRequestError({userMessage: 'Invalid Authorization value', statusCode: 400}); 11 | } 12 | 13 | this.application = application; 14 | this.id = parts[0]; 15 | this.secret = parts[1]; 16 | 17 | this.ttl = ttl || 3600; 18 | } 19 | 20 | BasicApiAuthenticator.prototype.authenticate = function authenticate(callback) { 21 | var self = this; 22 | 23 | self.application.getApiKey(self.id,function(err,apiKey){ 24 | if(err){ 25 | callback(err.status===404 ? new ApiAuthRequestError({userMessage: 'Invalid Client Credentials', error: 'invalid_client', statusCode: 401}) : err); 26 | }else{ 27 | if( 28 | (apiKey.secret===self.secret) && 29 | (apiKey.status==='ENABLED') && 30 | (apiKey.account.status==='ENABLED') 31 | ){ 32 | var authenticationResult = new AuthenticationResult(apiKey,self.application.dataStore); 33 | 34 | authenticationResult.application = self.application; 35 | authenticationResult.ttl = self.ttl; 36 | 37 | callback(null,authenticationResult); 38 | }else{ 39 | callback(new ApiAuthRequestError({userMessage: 'Invalid Client Credentials', error: 'invalid_client', statusCode: 401})); 40 | } 41 | } 42 | }); 43 | }; 44 | 45 | module.exports = BasicApiAuthenticator; 46 | -------------------------------------------------------------------------------- /lib/authc/BasicRequestAuthenticator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var RequestAuthenticator = require('./RequestAuthenticator'); 4 | var utils = require('../utils'); 5 | 6 | function BasicRequestAuthenticator() { 7 | BasicRequestAuthenticator.super_.apply(this, arguments); 8 | } 9 | utils.inherits(BasicRequestAuthenticator, RequestAuthenticator); 10 | 11 | BasicRequestAuthenticator.prototype.authenticate = function basicAuthenticate(request) { 12 | var concat = this.apiKey.id + ':' + this.apiKey.secret; 13 | var base64 = utils.base64.encode(concat); 14 | request.headers['Authorization'] = 'Basic ' + base64; 15 | }; 16 | 17 | module.exports = BasicRequestAuthenticator; 18 | -------------------------------------------------------------------------------- /lib/authc/ClientCredentialGrantAuthenticator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function OAuthClientCredentialGrantRequestAuthenticator(application) { 4 | if (!(this instanceof OAuthClientCredentialGrantRequestAuthenticator)) { 5 | return new OAuthClientCredentialGrantRequestAuthenticator(application); 6 | } 7 | this.application = application; 8 | } 9 | OAuthClientCredentialGrantRequestAuthenticator.prototype.authenticate = function authenticate() { 10 | // TODO 11 | }; 12 | 13 | module.exports = OAuthClientCredentialGrantRequestAuthenticator; 14 | -------------------------------------------------------------------------------- /lib/authc/RequestAuthenticator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function RequestAuthenticator(apiKey) { 4 | if (!apiKey) { 5 | throw new Error('apiKey is required.'); 6 | } 7 | if (!apiKey.id) { 8 | throw new Error('apiKey.id is required.'); 9 | } 10 | if (!apiKey.secret) { 11 | throw new Error('apiKey.secret is required.'); 12 | } 13 | this.apiKey = apiKey; 14 | } 15 | //All subclass types must have an 'authenticate' prototype function 16 | 17 | module.exports = RequestAuthenticator; -------------------------------------------------------------------------------- /lib/authc/StormpathAssertionAuthenticator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var njwt = require('njwt'); 4 | var AssertionAuthenticationResult = require('./AssertionAuthenticationResult'); 5 | 6 | /** 7 | * @class 8 | * 9 | * @description 10 | * 11 | * Creates an authenticator that can be used to verify a Stormpath Token that was 12 | * provided when the user returned from ID Site, or a SAML callback. This method 13 | * only verifies the token and provides you with any errors. After using this 14 | * authenticator you will likely want to use the {@link OAuthStormpathTokenAuthenticator} 15 | * to generate an Access + Refresh token pair for the user. 16 | * 17 | * Note: this authenticator is bound to the API Key Pair of the client that 18 | * fetched the application resource that you pass to the constructor. 19 | * 20 | * @param {Application} application The Stormpath Application to authenticate against. 21 | * 22 | * @example 23 | * var appHref = 'https://api.stormpath.com/v1/applications/3WIeKpaEjPHfLmy6GIvbwv'; 24 | * 25 | * client.getApplication(appHref, function(err, application) { 26 | * var authenticator = new stormpath.StormpathAssertionAuthenticator(application); 27 | * }); 28 | */ 29 | function StormpathAssertionAuthenticator(application) { 30 | Object.defineProperty(this, 'dataStore', { 31 | enumerable:false, 32 | value: application.dataStore 33 | }); 34 | 35 | this.secret = this.dataStore.requestExecutor.options.client.apiKey.secret; 36 | } 37 | 38 | /** 39 | * Exchange the Stormpath Token for an Access and Refresh token. 40 | * 41 | * @param {Object} tokenRequest 42 | * An object to encapsulate the request. 43 | * 44 | * @param {String} tokenRequest.stormpath_token 45 | * The Stormpath Token, from the ID Site or SAML callback. This is a compacted JWT string. 46 | * 47 | * @param {Function} callback 48 | * Callback function, will be called with (err, {@link AssertionAuthenticationResult}). 49 | * 50 | * @example 51 | * 52 | * var stormpathToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIzRHZNZ2JOVFEwZkhuS3BHd1VHUlB4IiwiaWF0IjoxNDcwMjU4MDc0LCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy8yNGs3SG5ET3o0dFE5QVJzbVZ6YUNJIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8xdWxlM3dKbkxZVUw3VVE2OGFBdlJaOWwiLCJleHAiOjE0NzAyNjk0MTJ9.i4OWcqczU-us71zT2XIiL69s2srJ7YPH5mAzrw8rNE8'; 53 | * 54 | * authenticator.authenticate(stormpathToken, function(err, assertionAuthenticationResult) { 55 | * assertionAuthenticationResult.getAccount(function(err, account){ 56 | * if (err) { 57 | * // an error occured on ID Site or during the SAML authentication flow 58 | * console.error(err); 59 | * return; 60 | * } 61 | * console.log(account.email + ' has authenticated'); 62 | * }); 63 | * }); 64 | */ 65 | StormpathAssertionAuthenticator.prototype.authenticate = function authenticate(stormpathToken, callback) { 66 | var dataStore = this.dataStore; 67 | njwt.verify(stormpathToken, this.secret, function (err, jwt) { 68 | if (err) { 69 | err.statusCode = 401; 70 | return callback(err); 71 | } 72 | 73 | if (jwt.body.err){ 74 | return callback(jwt.body.err); 75 | } 76 | 77 | var account = null; 78 | 79 | // For Stormpath mapped JWT fields, see: 80 | // https://docs.stormpath.com/rest/product-guide/latest/005_auth_n.html#step-5-stormpath-response-with-jwt 81 | if (jwt.body.sub) { 82 | account = { 83 | href: jwt.body.sub 84 | }; 85 | } 86 | 87 | callback(null, new AssertionAuthenticationResult( 88 | dataStore, { 89 | stormpath_token: stormpathToken, 90 | expandedJwt: jwt, 91 | account: account 92 | } 93 | )); 94 | }); 95 | 96 | return this; 97 | }; 98 | 99 | module.exports = StormpathAssertionAuthenticator; 100 | -------------------------------------------------------------------------------- /lib/authc/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BasicRequestAuthenticator = require('./BasicRequestAuthenticator'); 4 | var Sauthc1RequestAuthenticator = require('./Sauthc1RequestAuthenticator'); 5 | 6 | /** 7 | * @private 8 | * 9 | * @description 10 | * 11 | * A factory function that inspects any options and returns an appropriate {@link RequestAuthenticator} to use to 12 | * authenticate requests submitted to the API server. 13 | * 14 | * @param {object} options 15 | * @param {Sauthc1RequestAuthenticator|BasicRequestAuthenticator=} options.requestAuthenticator 16 | * @param {string} options.authenticationScheme - name of auth method that will instantiated 17 | * @returns {BasicRequestAuthenticator|Sauthc1RequestAuthenticator} 18 | */ 19 | function getAuthenticator(options) { 20 | 21 | if (options.requestAuthenticator) { 22 | return options.requestAuthenticator; 23 | } 24 | 25 | var apiKey = options.client ? options.client.apiKey : options.apiKey; 26 | 27 | if (!apiKey) { 28 | throw new Error('If you do not specify a \'requestAuthenticator\' field, you must specify an ApiKey.'); 29 | } 30 | 31 | var authc = new BasicRequestAuthenticator(apiKey); //default until Sauthc1 is working. 32 | 33 | if (options.authenticationScheme) { 34 | var scheme = options.authenticationScheme.toUpperCase(); 35 | 36 | if (scheme === 'SAUTHC1') { 37 | authc = new Sauthc1RequestAuthenticator(apiKey); 38 | } else if (scheme === 'BASIC') { 39 | authc = new BasicRequestAuthenticator(apiKey); 40 | } else { 41 | throw new Error("Unrecognized authentication scheme: " + options.authenticationScheme); 42 | } 43 | } 44 | 45 | return authc; 46 | } 47 | 48 | module.exports = { 49 | ApiKey: require('./ApiKey'), 50 | loadApiKey: require('./ApiKeyLoader'), 51 | getAuthenticator: getAuthenticator 52 | }; 53 | -------------------------------------------------------------------------------- /lib/cache/Cache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('underscore'); 4 | var CacheEntry = require('./CacheEntry'); 5 | var CacheStats = require('./CacheStats'); 6 | var MemoryStore = require('./MemoryStore'); 7 | 8 | var defaults = { 9 | store: MemoryStore, 10 | ttl: 5 * 60, 11 | tti: 5 * 60 12 | }; 13 | 14 | /** 15 | * @private 16 | * 17 | * @description 18 | * 19 | * Cache abstractions 20 | * A unified interface to different implementations of data caching. 21 | * 22 | * @param [store=MemoryStore] 23 | * @param {number=} [ttl=5*60] - time to live in seconds 24 | * @param {number=} [tti=5*60] - time to idle in seconds 25 | * @params {object} options - cache configuration options 26 | * @constructor 27 | */ 28 | function Cache(options) { 29 | options = options || {}; 30 | 31 | if (!(this instanceof Cache)) { 32 | return new Cache(options); 33 | } 34 | 35 | if (typeof options === 'function'){ 36 | options = { store: options }; 37 | } 38 | 39 | _.defaults(options, defaults); 40 | var Store = options.store; 41 | 42 | this.ttl = options.ttl; 43 | this.tti = options.tti; 44 | this.store = new Store(options); 45 | this.stats = new CacheStats(); 46 | } 47 | 48 | Cache.prototype.get = function (key, cb) { 49 | 50 | var self = this; 51 | 52 | self.store.get(key, function (err, entry) { 53 | if (err || !entry) { 54 | return cb(err, null); 55 | } 56 | 57 | if (entry.isExpired(self.ttl, self.tti)) { 58 | self.stats.miss(true); 59 | return self.store.delete(key, function (err) { 60 | return cb(err, null); 61 | }); 62 | } 63 | 64 | self.stats.hit(); 65 | // what point to touch entry if we not updating cache store 66 | // will work for memory store but will not for redis store 67 | 68 | if (self.ttl !== self.tti) { 69 | entry.touch(); 70 | self.store.set(key, entry, function(){}); 71 | } 72 | 73 | return cb(null, entry.value); 74 | }); 75 | }; 76 | 77 | /** 78 | * 79 | * @param key 80 | * @param value 81 | * @param {boolean|function} [_new=true] 82 | * @param {function} cb 83 | */ 84 | Cache.prototype.put = function (key, value, _new, cb) { 85 | if (typeof _new === 'function') { 86 | cb = _new; 87 | _new = true; 88 | } 89 | this.stats.put(_new); 90 | this.store.set(key, new CacheEntry(value), cb); 91 | }; 92 | 93 | Cache.prototype.delete = function (key, cb) { 94 | this.stats.delete(); 95 | this.store.delete(key, cb); 96 | }; 97 | 98 | Cache.prototype.clear = function (cb) { 99 | this.stats.clear(); 100 | this.store.clear(cb); 101 | }; 102 | 103 | Cache.prototype.size = function (cb) { 104 | this.store.size(cb); 105 | }; 106 | 107 | module.exports = Cache; 108 | -------------------------------------------------------------------------------- /lib/cache/CacheEntry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var moment = require('moment'); 4 | 5 | /** 6 | * @private 7 | * 8 | * @description 9 | * 10 | * Cache entry abstraction 11 | * A single entry inside a cache 12 | * 13 | * It contains the data as originally returned by Stormpath along 14 | * with additional metadata like timestamps. 15 | * @param {object} value - value to store in cache 16 | * @param {Date=} createdAt - time when cache entry created 17 | * @param {Date=} lastAccessedAt - time when cache entry was last accessed 18 | * @constructor 19 | */ 20 | function CacheEntry(value, createdAt, lastAccessedAt){ 21 | this.value = value; 22 | this.createdAt = createdAt || Date.now(); 23 | this.lastAccessedAt = lastAccessedAt || this.createdAt; 24 | 25 | if (typeof this.createdAt !== 'number' || 26 | typeof this.lastAccessedAt !== 'number'){ 27 | throw new Error('Expecting date in timestamp format or use CacheEntry.parse method instead'); 28 | } 29 | } 30 | 31 | // todo: what does it do in py: datetime.timedelta 32 | function timedelta(seconds){return seconds * 1000;} 33 | 34 | /** 35 | * Changes last accessed to current 36 | */ 37 | CacheEntry.prototype.touch = function touch(){ 38 | this.lastAccessedAt = Date.now(); 39 | }; 40 | 41 | /** 42 | * Checks is entry expired due to overdue of live or idle timeouts 43 | * @param {number=} ttl - time to live 44 | * @param {number=} tti - time to idle 45 | * @returns {boolean} 46 | */ 47 | CacheEntry.prototype.isExpired = function isExpired(ttl, tti){ 48 | var now = Date.now(); 49 | return (now >= (this.createdAt + timedelta(ttl))) || 50 | (now >= (this.lastAccessedAt + timedelta(tti))); 51 | }; 52 | 53 | CacheEntry.prototype.toObject = function(){ 54 | function printDate(val){ 55 | return moment.utc(val).format('YYYY-MM-DD HH:mm:ss.SSS'); 56 | } 57 | return { 58 | createdAt: printDate(this.createdAt), 59 | lastAccessedAt: printDate(this.lastAccessedAt), 60 | value: this.value 61 | }; 62 | }; 63 | 64 | CacheEntry.parse = function(data){ 65 | function parseDate(val){ 66 | return moment.utc(val).valueOf(); 67 | } 68 | return new CacheEntry(data.value, 69 | parseDate(data.createdAt), 70 | parseDate(data.lastAccessedAt)); 71 | }; 72 | 73 | module.exports = CacheEntry; 74 | -------------------------------------------------------------------------------- /lib/cache/CacheManager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cache = require('./Cache'); 4 | 5 | /** 6 | * @private 7 | * 8 | * @description 9 | * 10 | * Cache manager abstraction. 11 | * Handles all the different caches used by the SDK 12 | * @constructor 13 | */ 14 | function CacheManager() { 15 | var self = this; 16 | self.caches = {}; 17 | 18 | Object.defineProperty(self, 'stats', { 19 | get: function () { 20 | return Object.keys(self.caches).reduce(function(a,region){ 21 | a[region] = self.caches[region].stats; 22 | return a; 23 | },{}); 24 | } 25 | }); 26 | } 27 | 28 | CacheManager.prototype.createCache = function (region,options) { 29 | this.caches[region] = new Cache(options); 30 | }; 31 | 32 | CacheManager.prototype.getCache = function (region) { 33 | return this.caches[region]; 34 | }; 35 | 36 | module.exports = CacheManager; -------------------------------------------------------------------------------- /lib/cache/CacheStats.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @private 5 | * 6 | * @description 7 | * 8 | * Cache stats. 9 | * Represents cache statistic 10 | * @constructor 11 | */ 12 | function CacheStats() { 13 | this.puts = 0; 14 | this.hits = 0; 15 | this.misses = 0; 16 | this.expirations = 0; 17 | this.size = 0; 18 | } 19 | 20 | /** 21 | * 22 | * @param {boolean} [_new=true] 23 | */ 24 | CacheStats.prototype.put = function (_new) { 25 | _new = _new !== false; 26 | this.puts++; 27 | if (_new) { 28 | this.size++; 29 | } 30 | }; 31 | 32 | CacheStats.prototype.hit = function () { 33 | this.hits++; 34 | }; 35 | 36 | CacheStats.prototype.miss = function (expired) { 37 | this.misses++; 38 | if (expired) { 39 | this.expirations++; 40 | } 41 | }; 42 | 43 | CacheStats.prototype.delete = function () { 44 | if (this.size > 0) { 45 | this.size--; 46 | } 47 | }; 48 | 49 | CacheStats.prototype.clear = function () { 50 | this.size = 0; 51 | }; 52 | 53 | module.exports = CacheStats; 54 | -------------------------------------------------------------------------------- /lib/cache/DisabledCache.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function DisabledCache() { 4 | } 5 | 6 | DisabledCache.prototype.get = 7 | DisabledCache.prototype.set = 8 | DisabledCache.prototype.delete = 9 | DisabledCache.prototype.clear = 10 | DisabledCache.prototype.size = function () { 11 | return Array.prototype.slice.call(arguments, -1)[0](null, null); 12 | }; 13 | 14 | module.exports = DisabledCache; -------------------------------------------------------------------------------- /lib/cache/MemcachedStore.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Memcached = require('memcached'); 4 | 5 | // todo: create store provider and register redis store only for node.js version 6 | var CacheEntry = require('./CacheEntry'); 7 | 8 | /** 9 | * @class 10 | * 11 | * @private 12 | * 13 | * @description 14 | * 15 | * Caching implementation that uses Redis as data storage. If an existing 16 | * client instance is not provided, this constructor will create one. Utilizes 17 | * the [memcached library](https://github.com/3rd-Eden/memcached). 18 | * 19 | * @param {Object} [options] 20 | * @param {Object} [options.client] A memcached client instance 21 | * @param {Object|String|Array} [options.connection] Memcached client constructor 22 | * connection string parameter. 23 | * @param {*} [options.*] Other options to pass to the Memcached client constructor. 24 | */ 25 | function MemcachedStore(opt){ 26 | this._options = opt || {}; 27 | this.memcached = opt.client || MemcachedStore._createClient(opt); 28 | } 29 | 30 | MemcachedStore._createClient = function creteMemcachedClient(opt){ 31 | return new Memcached(opt.connection, opt.options); 32 | }; 33 | 34 | MemcachedStore.prototype.get = function (key, cb){ 35 | this.memcached.get(key, function(err, entry){ 36 | return cb(err, !entry ? null : CacheEntry.parse(entry)); 37 | }); 38 | }; 39 | 40 | MemcachedStore.prototype.set = function (key, val, cb){ 41 | this.memcached.set(key, val, this._options.ttl, cb); 42 | }; 43 | 44 | MemcachedStore.prototype.delete = function (key, cb){ 45 | this.memcached.del(key, cb); 46 | }; 47 | 48 | MemcachedStore.prototype.clear = function (cb){ 49 | this.memcached.flush(cb); 50 | }; 51 | 52 | MemcachedStore.prototype.size = function (cb){ 53 | this.memcached.stats(function(err, stats){ 54 | var size = stats && stats.length ? stats[0].curr_items : -1; 55 | cb(err, size); 56 | }); 57 | }; 58 | 59 | module.exports = MemcachedStore; 60 | -------------------------------------------------------------------------------- /lib/cache/MemoryStore.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function MemoryStore() { 4 | 5 | var store = {}; 6 | 7 | this.get = function get(key, cb) { 8 | return cb(null, store[key]); 9 | }; 10 | 11 | this.set = function set(key, val, cb) { 12 | store[key] = val; 13 | return cb(null, val); 14 | }; 15 | 16 | this.delete = function del(key, cb) { 17 | delete store[key]; 18 | return cb(null); 19 | }; 20 | 21 | this.clear = function clear(cb) { 22 | store = {}; 23 | cb(null); 24 | }; 25 | 26 | this.size = function dbsize(cb) { 27 | cb(null, Object.keys(store).length); 28 | }; 29 | } 30 | 31 | module.exports = MemoryStore; -------------------------------------------------------------------------------- /lib/cache/RedisStore.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var redis = require('redis'); 4 | 5 | var CacheEntry = require('./CacheEntry'); 6 | 7 | /** 8 | * @class 9 | * 10 | * @private 11 | * 12 | * @description 13 | * 14 | * Caching implementation that uses Redis as data storage. If an existing 15 | * client instance is not provided, this constructor will create one. Utilizes 16 | * the [node-redis](https://github.com/NodeRedis/node_redis) library. 17 | * 18 | * @param {Object} [options] 19 | * @param {Object} [options.client] A redis client instance 20 | * @param {Object} [options.connection] Redis client constructor connection options 21 | * @param {Number} [options.connection.port] DB port number 22 | * @param {String} [options.connection.host] DB host name 23 | * @param {*} [options.*] Other options to pass to the Redis client constructor. 24 | */ 25 | function RedisStore(opt){ 26 | this._options = opt || {}; 27 | this.redis = opt.client || RedisStore._createClient(opt); 28 | this.redis.debug_mode = /redis/i.test(process.env.DEBUG); 29 | } 30 | 31 | RedisStore._createClient = function createRedisClient(opt){ 32 | var conn = (opt && opt.connection) || {port:'6379',host:'127.0.0.1'}; 33 | return redis.createClient(conn.port, conn.host, opt.options); 34 | }; 35 | 36 | RedisStore.prototype.get = function (key, cb){ 37 | this.redis.get(key, function(err, entry){ 38 | return cb(err, !entry ? null : CacheEntry.parse(JSON.parse(entry))); 39 | }); 40 | }; 41 | 42 | RedisStore.prototype.set = function (key, val, cb){ 43 | var entry = JSON.stringify(val); 44 | this.redis.set(key, entry, cb); 45 | if (this._options.ttl){ 46 | this.redis.expire(key, this._options.ttl); 47 | } 48 | }; 49 | 50 | RedisStore.prototype.delete = function (key, cb){ 51 | this.redis.del(key, cb); 52 | }; 53 | 54 | RedisStore.prototype.clear = function (cb){ 55 | this.redis.flushdb(cb); 56 | }; 57 | 58 | RedisStore.prototype.size = function (cb){ 59 | this.redis.dbsize(cb); 60 | }; 61 | 62 | module.exports = RedisStore; 63 | -------------------------------------------------------------------------------- /lib/cache/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | CacheHandler: require('./CacheHandler'), 5 | CacheManager: require('./CacheManager'), 6 | Cache: require('./Cache'), 7 | CacheEntry: require('./CacheEntry'), 8 | CacheStats: require('./CacheStats'), 9 | DisabledCache: require('./DisabledCache'), 10 | MemoryStore: require('./MemoryStore') 11 | }; 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # 'apiKey' might feel redundant since 'client.apiKey' is what is being used. 3 | # But for backward-compability we need to have this here. 4 | apiKey: 5 | file: null 6 | id: null 7 | secret: null 8 | client: 9 | apiKey: 10 | file: null 11 | id: null 12 | secret: null 13 | cacheManager: 14 | defaultTtl: 300 15 | defaultTti: 300 16 | caches: 17 | account: 18 | ttl: 300 19 | tti: 300 20 | baseUrl: "https://api.stormpath.com/v1" 21 | connectionTimeout: 30 22 | authenticationScheme: "SAUTHC1" 23 | proxy: 24 | port: null 25 | host: null 26 | username: null 27 | password: null 28 | application: 29 | name: null 30 | href: null 31 | -------------------------------------------------------------------------------- /lib/configLoader.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var stormpathConfig = require('stormpath-config'); 4 | var strategy = stormpathConfig.strategy; 5 | 6 | // Set the paths the we want to load configuration files from 7 | var currentPath = process.cwd(); 8 | var stormpathPath = '~/.stormpath'; 9 | 10 | // Create a default client config loader. 11 | module.exports = function (extendWithConfig) { 12 | return new stormpathConfig.Loader([ 13 | // Load default configuration. 14 | new strategy.LoadFileConfigStrategy(path.join(__dirname, '/config.yml'), true), 15 | 16 | // Load API keys and configuration from home (.stormpath) folder 17 | new strategy.LoadAPIKeyConfigStrategy(stormpathPath + '/apiKey.properties'), 18 | new strategy.LoadFileConfigStrategy(stormpathPath + '/stormpath.json'), 19 | new strategy.LoadFileConfigStrategy(stormpathPath + '/stormpath.yml'), 20 | 21 | // Load API keys and configuration from app folder 22 | new strategy.LoadAPIKeyConfigStrategy(currentPath + '/apiKey.properties'), 23 | new strategy.LoadFileConfigStrategy(currentPath + '/stormpath.json'), 24 | new strategy.LoadFileConfigStrategy(currentPath + '/stormpath.yml'), 25 | 26 | // Load configuration from our environment. 27 | new strategy.LoadEnvConfigStrategy('STORMPATH', { 28 | // Aliases used to support legacy API key. 29 | STORMPATH_APIKEY_ID: 'STORMPATH_API_KEY_ID', 30 | STORMPATH_APIKEY_SECRET: 'STORMPATH_API_KEY_SECRET', 31 | STORMPATH_APIKEY_FILE: 'STORMPATH_API_KEY_FILE' 32 | }), 33 | 34 | // Extend our configuration with the configuration we passed into the client. 35 | // Also, try and load our API key if it was specified in our config. 36 | new strategy.ExtendConfigStrategy(extendWithConfig), 37 | new strategy.LoadAPIKeyFromConfigStrategy(), 38 | 39 | // Enrich our client config. 40 | new strategy.EnrichClientConfigStrategy(), 41 | 42 | // Validate config so that we know that we have an API key and can continue... 43 | new strategy.ValidateClientConfigStrategy() 44 | 45 | ]); 46 | }; 47 | -------------------------------------------------------------------------------- /lib/ds/NonceStore.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Deafult NonceStore - requires a data store instance which has a cache manager 5 | */ 6 | var NonceStore = function NonceStore(dataStore){ 7 | this.cache = dataStore.cacheHandler.cacheManager.getCache('idSiteNonces'); 8 | }; 9 | 10 | NonceStore.prototype.getNonce = function getNonce(nonceValue,cb) { 11 | this.cache.get(nonceValue,cb); 12 | }; 13 | 14 | NonceStore.prototype.putNonce = function putNonce(nonceValue,cb) { 15 | this.cache.put(nonceValue,nonceValue,cb); 16 | }; 17 | 18 | 19 | module.exports = NonceStore; -------------------------------------------------------------------------------- /lib/error/ApiAuthRequestError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | function ApiAuthRequestError(errorData){ 6 | Error.captureStackTrace(this, this.constructor); 7 | 8 | this.name = this.constructor.name; 9 | this.userMessage = this.message = errorData.userMessage; 10 | this.statusCode = errorData.statusCode || 400; 11 | this.error = errorData.error; 12 | } 13 | utils.inherits(ApiAuthRequestError, Error); 14 | 15 | module.exports = ApiAuthRequestError; 16 | -------------------------------------------------------------------------------- /lib/error/ResourceError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | function ResourceError(responseBody, requestData) { 6 | Error.captureStackTrace(this, this.constructor); 7 | 8 | requestData = requestData || {}; 9 | 10 | this.name = this.constructor.name; 11 | this.status = responseBody.status; 12 | this.code = responseBody.code; 13 | this.userMessage = responseBody.message; 14 | this.developerMessage = responseBody.developerMessage; 15 | this.moreInfo = responseBody.moreInfo; 16 | this.requestId = responseBody.requestId; 17 | this.url = requestData.url; 18 | this.method = requestData.method; 19 | this.stack = ''; 20 | 21 | this.message = 'HTTP ' + this.status + 22 | ', Stormpath ' + this.code + ' (' + this.moreInfo + '): ' + 23 | this.developerMessage; 24 | 25 | if (responseBody.error) { 26 | this.error = responseBody.error; 27 | } 28 | } 29 | utils.inherits(ResourceError, Error); 30 | 31 | module.exports = ResourceError; 32 | -------------------------------------------------------------------------------- /lib/error/invalid-href.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Produces an Erorr object with a message that indicates which resource type has 5 | * violated an href validation. 6 | * 7 | * @param {string} href The href that was passed into a resource getter. 8 | * @param {string} resourceName The human readable name of the expected resource type. 9 | * @return {Error} A simple error with the message 10 | */ 11 | function InvalidHrefError(href, resourceName){ 12 | var message = 'Argument \'href\' (' + href + ') is not a valid ' + resourceName +' href.'; 13 | return new Error(message); 14 | } 15 | 16 | module.exports = InvalidHrefError; 17 | -------------------------------------------------------------------------------- /lib/error/messages.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ID_SITE_INVALID_CB_URI: "cb_uri URI must be provided and must be in your ID Site whitelist", 3 | ID_SITE_JWT_INVALID_AUD: "The client used to sign the jwtResponse is different than the one used in this datasore.", 4 | ID_SITE_JWT_HAS_EXPIRED: "Token has expired", 5 | ID_SITE_JWT_ALREADY_USED: "Token has already been used." 6 | }; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./stormpath'); 4 | -------------------------------------------------------------------------------- /lib/oauth/id-site-grant.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var JwtAuthenticationResult = require('../jwt/jwt-authentication-result'); 5 | 6 | function OAuthIdSiteTokenGrantAuthenticationResult(application, data){ 7 | OAuthIdSiteTokenGrantAuthenticationResult.super_.apply(this, arguments); 8 | this.accessTokenResponse = data; 9 | } 10 | util.inherits(OAuthIdSiteTokenGrantAuthenticationResult, JwtAuthenticationResult); 11 | 12 | /** 13 | * Deprecated, replaced by OAuthStormpathTokenAuthenticator. Will be removed in 1.0. 14 | * 15 | * @private 16 | */ 17 | function OAuthIdSiteTokenGrantAuthenticator(application) { 18 | if (!(this instanceof OAuthIdSiteTokenGrantAuthenticator)) { 19 | return new OAuthIdSiteTokenGrantAuthenticator(application); 20 | } 21 | 22 | this.application = application; 23 | } 24 | 25 | OAuthIdSiteTokenGrantAuthenticator.prototype.authenticate = util.deprecate(function authenticate(data, callback) { 26 | var application = this.application; 27 | 28 | var formData = { 29 | grant_type: 'id_site_token', 30 | token: data.id_site_token 31 | }; 32 | 33 | var tokenHref = application.href + '/oauth/token'; 34 | 35 | application.dataStore.createResource(tokenHref, { form: formData }, function(err, tokenData) { 36 | if (err) { 37 | return callback(err); 38 | } 39 | 40 | callback(null, new OAuthIdSiteTokenGrantAuthenticationResult(application, tokenData)); 41 | }); 42 | },'OAuthIdSiteTokenGrantAuthenticator is deprecated. Use OAuthStormpathTokenAuthenticator instead. See http://docs.stormpath.com/nodejs/api/oauthStormpathTokenAuthenticator'); 43 | 44 | module.exports = { 45 | authenticator: OAuthIdSiteTokenGrantAuthenticator, 46 | authenticationResult: OAuthIdSiteTokenGrantAuthenticationResult 47 | }; 48 | -------------------------------------------------------------------------------- /lib/oauth/stormpath-access-token-authentication-result.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | /** 6 | * @constructor 7 | * 8 | * @description 9 | * 10 | * Encapsulates the access token resource response, obtained from the `/accessTokens` collection. 11 | * 12 | * @param {Client} client 13 | * An initialized Stormpath Client for the tenant the issued the token. 14 | * 15 | * @param {AccessTokenResponse} accessTokenResponse 16 | * The access token response from the Stormpath REST API. 17 | */ 18 | function StormpathAccessTokenAuthenticationResult(client, data) { 19 | if (!(this instanceof StormpathAccessTokenAuthenticationResult)) { 20 | return new StormpathAccessTokenAuthenticationResult(client, data); 21 | } 22 | 23 | Object.defineProperty(this, 'client', { 24 | enumerable: false, 25 | value: client 26 | }); 27 | 28 | for (var key in data) { 29 | if (data.hasOwnProperty(key)) { 30 | this[key] = data[key]; 31 | } 32 | } 33 | } 34 | 35 | /** 36 | * @name StormpathAccessTokenAuthenticationResult#account 37 | * 38 | * @description 39 | * 40 | * An object literal with an href pointer to the account that has authenticated. 41 | * Use {@link StormpathAccessTokenAuthenticationResult#getAccount StormpathAccessTokenAuthenticationResult.getAccount()} 42 | * to fetch the full {@link Account} resource. 43 | * 44 | * @type {Object} 45 | */ 46 | StormpathAccessTokenAuthenticationResult.prototype.account = null; 47 | 48 | /** 49 | * An object literal with an href pointer to the application that issued this 50 | * token. Use {@link StormpathAccessTokenAuthenticationResult#getApplication StormpathAccessTokenAuthenticationResult.getApplication()} 51 | * to fetch the full {@link Application} resource. 52 | * 53 | * @type {Object} 54 | */ 55 | StormpathAccessTokenAuthenticationResult.prototype.application = null; 56 | 57 | /** 58 | * @name StormpathAccessTokenAuthenticationResult#jwt 59 | * 60 | * @description 61 | * 62 | * The JWT access token string that was provided for authentication. 63 | * 64 | * @type {String} 65 | */ 66 | StormpathAccessTokenAuthenticationResult.prototype.jwt = null; 67 | 68 | /** 69 | * @name StormpathAccessTokenAuthenticationResult#expandedJwt 70 | * 71 | * @description 72 | * 73 | * An object that allows you to inspect the body, claims, and header of the 74 | * access token. 75 | * 76 | * @type {Object} 77 | */ 78 | StormpathAccessTokenAuthenticationResult.prototype.expandedJwt = null; 79 | 80 | /** 81 | * @function 82 | * 83 | * @description Get the account resource of the account that has authenticated. 84 | * 85 | * @param {ExpansionOptions} options 86 | * Options for expanding the fetched {@link Account} resource. 87 | * 88 | * @param {Function} callback 89 | * The callback to call with the parameters (err, {@link Account}). 90 | */ 91 | StormpathAccessTokenAuthenticationResult.prototype.getAccount = function getAccount( /* [options,] callback */ ) { 92 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 93 | this.client.getAccount(this.account.href, args.options, require('../resource/Account'), args.callback); 94 | }; 95 | 96 | StormpathAccessTokenAuthenticationResult.prototype.getApplication = function getApplication( /* [options,] callback */ ) { 97 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 98 | 99 | this.client.getApplication(this.application.href, args.options, require('../resource/Application'), args.callback); 100 | }; 101 | 102 | module.exports = StormpathAccessTokenAuthenticationResult; 103 | -------------------------------------------------------------------------------- /lib/proxy/ObjectCallProxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a new ObjectCallProxy. 3 | * 4 | * @class 5 | * 6 | * @private 7 | */ 8 | function ObjectCallProxy (source) { 9 | this.source = source; 10 | this.attached = {}; 11 | this.pending = []; 12 | } 13 | 14 | /** 15 | * Restore all of the attached methods. 16 | * 17 | * @private 18 | */ 19 | ObjectCallProxy.prototype._restore = function () { 20 | for (var name in this.attached) { 21 | this.source[name] = this.attached[name]; 22 | delete this.attached[name]; 23 | } 24 | }; 25 | 26 | /** 27 | * Release all of the pending callbacks. 28 | * 29 | * @private 30 | * @param {Error} [err] - Error to release all callbacks with (optional). 31 | */ 32 | ObjectCallProxy.prototype._release = function (err) { 33 | var source = this.source; 34 | var pending = this.pending; 35 | 36 | this.pending = []; 37 | 38 | pending.forEach(function (call) { 39 | var fn = call.fn; 40 | var args = call.args; 41 | var callback = call.callback; 42 | 43 | args[args.length - 1] = function () { 44 | var subArgs = err ? [err] : Array.prototype.slice.call(arguments); 45 | callback.apply(null, subArgs); 46 | }; 47 | 48 | fn.apply(source, args); 49 | }); 50 | }; 51 | 52 | /** 53 | * Attach onto the source object and intercept all calling 54 | * methods with callbacks (where last argument is function). 55 | * 56 | * @param fn [predicateFn] - Predicate to filter methods by (optional). 57 | */ 58 | ObjectCallProxy.prototype.attach = function (predicateFn) { 59 | var source = this.source; 60 | var pending = this.pending; 61 | var attached = this.attached; 62 | 63 | if (!predicateFn) { 64 | predicateFn = function () { 65 | return true; 66 | }; 67 | } 68 | 69 | function _attach (name) { 70 | if (!(name in attached)) { 71 | var originalFn = source[name]; 72 | 73 | attached[name] = originalFn; 74 | 75 | source[name] = function () { 76 | var args = Array.prototype.slice.call(arguments); 77 | var lastArgumentOffset = args.length - 1; 78 | 79 | if (args.length && typeof args[lastArgumentOffset] === 'function') { 80 | pending.push({ 81 | args: args, 82 | callback: args[lastArgumentOffset], 83 | fn: originalFn 84 | }); 85 | } else { 86 | return originalFn.apply(source, args); 87 | } 88 | }; 89 | } 90 | } 91 | 92 | // Proxy all methods on source object. 93 | for (var name in source) { 94 | var value = source[name]; 95 | 96 | // If a predicate is provided, then filter accordingly. 97 | if (!predicateFn(name)) { 98 | continue; 99 | } 100 | 101 | if (typeof value === 'function') { 102 | _attach(name); 103 | } 104 | } 105 | }; 106 | 107 | /** 108 | * Detach all attached methods and release any pending calls. 109 | * 110 | * @param {Error} [err] - Error to release all callbacks with (optional). 111 | */ 112 | ObjectCallProxy.prototype.detach = function (err) { 113 | this._restore(); 114 | this._release(err); 115 | }; 116 | 117 | module.exports = ObjectCallProxy; 118 | -------------------------------------------------------------------------------- /lib/resource/AccessToken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | /** 6 | * @class AccessToken 7 | * 8 | * @description 9 | * 10 | * Encapsulates a Stormpath OAuth Access Token Resource. For full 11 | * documentation of this resource, please see 12 | * [REST API Reference: Access tokens](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#access-tokens). 13 | * 14 | * This class should not be manually constructed. It should be obtained from one 15 | * of these methods: 16 | * 17 | * - From the client, using the access token stormpath href {@link Client#getAccessToken Client.getAccessToken()} 18 | * - From the account, {@link Account#getAccessTokens Account.getAccessTokens()} 19 | * - From a JWT authentication, {@link JwtAuthenticator#authenticate JwtAuthenticator} 20 | * - From an OAuth 2.0 password grant authentication, {@link OAuthPasswordGrantRequestAuthenticator#authenticate OAuthPasswordGrantRequestAuthenticator} 21 | * 22 | * For a high-level overview of token management, please see 23 | * {@link https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#introduction-to-token-based-authentication Introduction to Token-Based Authentication}. 24 | * 25 | * To revoke a access token, invoke the `delete()` method on an instance of 26 | * this class. 27 | * 28 | * @param {Object} accessTokenResource 29 | * 30 | * The JSON representation of this resource, retrieved from the Stormpath REST API. 31 | */ 32 | function AccessToken() { 33 | AccessToken.super_.apply(this, arguments); 34 | } 35 | 36 | // TODO: implement getAccount(), getRefreshToken() 37 | 38 | utils.inherits(AccessToken, require('./InstanceResource')); 39 | 40 | module.exports = AccessToken; 41 | 42 | /** 43 | * Deletes this resource from the API. 44 | * 45 | * @method AccessToken.delete 46 | * 47 | * @param {Function} callback 48 | * The function to call when the delete operation is complete. Will be called 49 | * with the parameter (err). 50 | */ 51 | -------------------------------------------------------------------------------- /lib/resource/AccountCreationPolicy.js: -------------------------------------------------------------------------------- 1 | var InstanceResource = require('./InstanceResource'); 2 | var utils = require('../utils'); 3 | 4 | /** 5 | * @class AccountCreationPolicy 6 | * 7 | * @description 8 | * Encapsulates the account creation policy of a {@link Directory}. For full documentation of the this resource, please see 9 | * [REST API Reference: Account Creation Policy](https://docs.stormpath.com/rest/product-guide/latest/reference.html#account-creation-policy). 10 | * 11 | * For a high-level overview account verification workflows, please see: 12 | * - [How to Verify an Account’s Email](https://docs.stormpath.com/rest/product-guide/latest/accnt_mgmt.html#how-to-verify-an-account-s-email). 13 | * - [Customizing Stormpath Emails via REST](https://docs.stormpath.com/rest/product-guide/latest/accnt_mgmt.html#customizing-stormpath-emails-via-rest). 14 | * 15 | * This class should not be manually constructed. It should be obtained from one of these methods: 16 | * - {@link Directory#getAccountCreationPolicy Directory.getAccountCreationPolicy()} 17 | * 18 | * @param {Object} accountCreationPolicyResource 19 | * The JSON representation of this resource, retrieved the Stormpath REST API. 20 | */ 21 | 22 | /** 23 | * @method AccountCreationPolicy.save 24 | * 25 | * @description 26 | * 27 | * Save changes to this resource. 28 | * 29 | * @param {Function} callback 30 | * The function to call when the save operation is complete. Will be called 31 | * with the parameters (err, updatedResource). 32 | */ 33 | 34 | 35 | function AccountCreationPolicy() { 36 | AccountCreationPolicy.super_.apply(this, arguments); 37 | } 38 | 39 | utils.inherits(AccountCreationPolicy, InstanceResource); 40 | 41 | module.exports = AccountCreationPolicy; -------------------------------------------------------------------------------- /lib/resource/AccountLink.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class AccountLink 8 | * 9 | * @description 10 | * 11 | * Encapsulates an AccountLink resource. For full documentation of this resource, 12 | * please see 13 | * [REST API Reference: AccountLink](https://docs.stormpath.com/rest/product-guide/latest/reference.html#account-link). 14 | * 15 | * For information about automatically generating account links, please see 16 | * {@link https://docs.stormpath.com/rest/product-guide/latest/accnt_mgmt.html#account-linking-automatic Automatic Account Linking}. 17 | * 18 | * This class should not be manually constructed. It should be obtained from one 19 | * of these methods: 20 | * 21 | * - {@link Account#getAccountLinks Account.getAccountLinks()} 22 | * - {@link Account#createAccountLink Account.createAccountLink()} 23 | * - {@link Tenant#createAccountLink Tenant.createAccountLink()} 24 | * 25 | * @augments {InstanceResource} 26 | * 27 | * @param {Object} accountLinkResource 28 | * 29 | * The JSON representation of this resource. 30 | */ 31 | function AccountLink() { 32 | AccountLink.super_.apply(this, arguments); 33 | } 34 | 35 | utils.inherits(AccountLink, InstanceResource); 36 | 37 | /** 38 | * Retrieves the "left" account of this account link. The "left" and "right" 39 | * designations are purely arbitrary and imply no hierarchy or priority between 40 | * the two. 41 | * 42 | * @param {ExpansionOptions} [options] 43 | * For retrieving linked resources of the query result. 44 | * 45 | * @param {Function} callback 46 | * The function to call when the operation is complete. Will be called with 47 | * the parameters (err, {@link Account}). 48 | */ 49 | AccountLink.prototype.getLeftAccount = function getLeftAccount(/* [options], callback */) { 50 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 51 | 52 | return this.dataStore.getResource(this.leftAccount.href, args.options, require('./Account'), args.callback); 53 | }; 54 | 55 | /** 56 | * Retrieves the "right" account of this account link. The "left" and "right" 57 | * designations are purely arbitrary and imply no hierarchy or priority between 58 | * the two. 59 | * 60 | * @param {ExpansionOptions} [options] 61 | * For retrieving linked resources of the query result. 62 | * 63 | * @param {Function} callback 64 | * The function to call when the operation is complete. Will be called with 65 | * the parameters (err, {@link Account}). 66 | */ 67 | AccountLink.prototype.getRightAccount = function getRightAccount(/* [options], callback */) { 68 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 69 | 70 | return this.dataStore.getResource(this.rightAccount.href, args.options, require('./Account'), args.callback); 71 | }; 72 | 73 | module.exports = AccountLink; 74 | -------------------------------------------------------------------------------- /lib/resource/AccountLinkingPolicy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class AccountLinkingPolicy 8 | * 9 | * @description 10 | * 11 | * Encapsulates an AccountLinkingPolicy resource. For full documentation of this resource, 12 | * please see 13 | * [REST API Reference: Account LinkingPolicy](https://docs.stormpath.com/rest/product-guide/latest/reference.html#account-linking-policy). 14 | * 15 | * This class should not be manually constructed. It should be obtained from one 16 | * of these methods: 17 | * 18 | * - {@link Application#getAccountLinkingPolicy Application.getAccountLinkingPolicy()} 19 | * - {@link Organization#getAccountLinkingPolicy Organization.getAccountLinkingPolicy()} 20 | * 21 | * Furthermore, an Account Linking Policy cannot be created or deleted from the SDK. Instead, it is 22 | * automatically created for all {@link Application} and {@link Organization} resources, 23 | * but can then be modified. 24 | * 25 | * @augments {InstanceResource} 26 | * 27 | * @param {Object} AccountLinkingPolicyResource 28 | * 29 | * The JSON representation of this resource. 30 | * 31 | */ 32 | function AccountLinkingPolicy() { 33 | AccountLinkingPolicy.super_.apply(this, arguments); 34 | } 35 | 36 | utils.inherits(AccountLinkingPolicy, InstanceResource); 37 | 38 | /** 39 | * Retrieves this account linking policy's associated tenant. 40 | * 41 | * @param {ExpansionOptions} options 42 | * Options for retrieving linked resources of the {@link Tenant} during this request. 43 | * 44 | * @param {Function} callback 45 | * The callback that will be called with the parameters (err, {@link Tenant}). 46 | */ 47 | AccountLinkingPolicy.prototype.getTenant = function getTenant(/* [options,] callback */) { 48 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 49 | 50 | return this.dataStore.getResource(this.tenant.href, args.options, require('./Tenant'), args.callback); 51 | }; 52 | 53 | // Removes the inherited delete method. This resource cannot be deleted manually! 54 | AccountLinkingPolicy.prototype.delete = undefined; 55 | 56 | module.exports = AccountLinkingPolicy; 57 | -------------------------------------------------------------------------------- /lib/resource/ApplicationAccountStoreMapping.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var AccountStoreMapping = require('./AccountStoreMapping'); 5 | 6 | /** 7 | * @class 8 | * 9 | * @augments {AccountStoreMapping} 10 | * 11 | * @description 12 | * 13 | * This object encapsulates an Application Account Store Mapping, which 14 | * represents the link between an {@link Application} and an Account Store, such 15 | * as a {@link Directory}, {@link Group}, or {@link Organization}. For full 16 | * documentation of this resource, please see 17 | * [REST API Reference: Account Store Mapping](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#account-store-mapping). 18 | * 19 | * This class should not be manually constructed. It should be obtained from one 20 | * of these methods: 21 | * 22 | * - {@link Application#createAccountStoreMapping Application.createAccountStoreMapping()} 23 | * - {@link Application#getAccountStoreMappings Application.getAccountStoreMappings()} 24 | * 25 | * For more information about account store mappings, please see 26 | * [Modeling Your User Base](https://docs.stormpath.com/rest/product-guide/latest/accnt_mgmt.html#modeling-your-user-base). 27 | * 28 | * @param {object} accountStoreMappingResource 29 | * 30 | * The JSON representation of this resource. 31 | */ 32 | function ApplicationAccountStoreMapping() { 33 | ApplicationAccountStoreMapping.super_.apply(this, arguments); 34 | } 35 | 36 | utils.inherits(ApplicationAccountStoreMapping, AccountStoreMapping); 37 | 38 | /** 39 | * @description 40 | * 41 | * Gets the {@link Application} that is associated with this account store mapping. 42 | * 43 | * @param {ExpansionOptions} [expansionOptions] 44 | * For retrieving linked resources of the {@link Application} during this request. 45 | * 46 | * @param {Function} callback 47 | * The callback to call when the operation is complete. Will be called with 48 | * (err, {@link Application}). 49 | */ 50 | ApplicationAccountStoreMapping.prototype.getApplication = function getApplication(/* [options,] callback */) { 51 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 52 | return this.dataStore.getResource(this.application.href, args.options, require('./Application'), args.callback); 53 | }; 54 | 55 | /** 56 | * This is not necessary, application can be passed to createAccountStoreMapping(). Remove in 1.0 57 | * 58 | * @private 59 | */ 60 | ApplicationAccountStoreMapping.prototype.setApplication = function setApplication(application) { 61 | this.application = { href: application.href }; 62 | return this; 63 | }; 64 | 65 | module.exports = ApplicationAccountStoreMapping; 66 | -------------------------------------------------------------------------------- /lib/resource/DirectoryChildResource.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class DirectoryChildResource 8 | */ 9 | function DirectoryChildResource() { 10 | DirectoryChildResource.super_.apply(this, arguments); 11 | } 12 | utils.inherits(DirectoryChildResource, InstanceResource); 13 | 14 | /** 15 | * Creates a group membership for a specific account. 16 | * 17 | * @private 18 | * 19 | * @param {Account|Object} account 20 | * An existing instance of {@link Account}, or an object literal with 21 | * an `href` property that identifies the account to add. 22 | * 23 | * @param {Group|Object} group 24 | * An existing instance of the group to create membership in, or an object literal with 25 | * an `href` property that identifies the group to use. 26 | * 27 | * @param {Object} options 28 | * Group membership options. 29 | * 30 | * @param {Function} callback 31 | * The function to call when the operation is complete. Will be called with the 32 | * parameters (err, {@link GroupMembership}). 33 | */ 34 | DirectoryChildResource.prototype._createGroupMembership = function createGroupMembership(account, group, options, callback) { 35 | var href = '/groupMemberships'; 36 | 37 | var membership = { 38 | account: { 39 | href: account.href 40 | }, 41 | group: { 42 | href: group.href 43 | } 44 | }; 45 | 46 | return this.dataStore.createResource(href, options, membership, require('./GroupMembership'), callback); 47 | }; 48 | 49 | /** 50 | * Gets the directory resource that is a parent of resource. 51 | * 52 | * @param {ExpansionOptions} [expansionOptions] 53 | * For retrieving linked resources of the {@link Directory} during this request. 54 | * 55 | * @param {Function} callback 56 | * The callback that will be called with the parameters (err, {@link Directory}). 57 | */ 58 | DirectoryChildResource.prototype.getDirectory = function getDirectoryChildResourceDirectory(/* [options,] callback */) { 59 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 60 | return this.dataStore.getResource(this.directory.href, args.options, require('./Directory'), args.callback); 61 | }; 62 | 63 | /** 64 | * Gets the Stormpath tenant resource that owns this resource. 65 | * 66 | * @param {ExpansionOptions} [expansionOptions] 67 | * For retrieving linked resources of the {@link Tenant} during this request. 68 | * 69 | * @param {Function} callback 70 | * The callback that will be called with the parameters (err, {@link Tenant}). 71 | */ 72 | DirectoryChildResource.prototype.getTenant = function getDirectoryChildResourceTenant(/* [options,] callback */) { 73 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 74 | return this.dataStore.getResource(this.tenant.href, args.options, require('./Tenant'), args.callback); 75 | }; 76 | 77 | /** 78 | * Gets the {@link CustomData} object for this resource. 79 | * 80 | * @param {Function} callback 81 | * The callback that will be called with the parameters (err, {@link CustomData}). 82 | */ 83 | DirectoryChildResource.prototype.getCustomData = function getCustomData(/* [options,] callback */) { 84 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 85 | return this.dataStore.getResource(this.customData.href, args.options, require('./CustomData'), args.callback); 86 | }; 87 | 88 | module.exports = DirectoryChildResource; 89 | -------------------------------------------------------------------------------- /lib/resource/Factor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class Factor 8 | * 9 | * @description 10 | * 11 | * Encapsulates a Factor resource, used for purposes of Multi-Factor Authentication. 12 | * For full documentation of this resource, 13 | * please see 14 | * [REST API Reference: Factor](https://docs.stormpath.com/rest/product-guide/latest/reference.html#ref-factor). 15 | * 16 | * This class should not be manually constructed. It should be obtained from one 17 | * of these methods: 18 | * 19 | * - {@link Account#createFactor Account.createFactor()} 20 | * - {@link Account#getFactors Account.getFactors()} 21 | * - {@link Challenge#getFactor Challenge.getFactor()} 22 | * - {@link Client#getFactor Client.getFactor()} 23 | * 24 | * Additionally, raw instances of Factor should never be used. Instead, it is extended 25 | * by concrete {@link SmsFactor} or {@link GoogleAuthenticatorFactor} resources, which 26 | * should be used instead, and will be passed to all callbacks. 27 | * 28 | * @augments {InstanceResource} 29 | * 30 | * @param {Object} factorResource 31 | * 32 | * The JSON representation of this resource. 33 | */ 34 | function Factor() { 35 | Factor.super_.apply(this, arguments); 36 | } 37 | 38 | utils.inherits(Factor, InstanceResource); 39 | 40 | /** 41 | * Retrieves the {@link Account} this factor belongs to. 42 | * 43 | * @param {ExpansionOptions} [expansionOptions] 44 | * For retrieving linked resources of the {@link Account} for this request. 45 | * This resource supports expansions for `customData`, `tenant`, `directory`, 46 | * `groups`, and `groupMemberships`. Groups and group memberships can also be 47 | * expanded and paginated. 48 | * 49 | * @param {Function} callback 50 | * The function to call when the operation is complete. Will be called with 51 | * (err, {@link Account}). 52 | */ 53 | Factor.prototype.getAccount = function getFactorAccount(/* [options,] callback */) { 54 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 55 | return this.dataStore.getResource(this.account.href, args.options, require('./Account'), args.callback); 56 | }; 57 | 58 | /** 59 | * Retrieves a list of challenges for this factor. 60 | * 61 | * @param {CollectionQueryOptions} options 62 | * Options for querying, filtering, sorting, paginating and expanding the query. It can be expanded 63 | * on `account` and `factor` fields. 64 | * 65 | * @param {Function} callback 66 | * The function to call when the operation is complete. Will be called 67 | * with the parameters (err, {@link CollectionResource}). The collection will 68 | * be a list of {@link Challenge} objects. 69 | * 70 | */ 71 | Factor.prototype.getChallenges = function getFactorChallenges(/* [options], callback */) { 72 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 73 | return this.dataStore.getResource(this.challenges.href, args.options, require('./Challenge'), args.callback); 74 | }; 75 | 76 | /** 77 | * Retrieves the most recent challenge for this factor, or null if none is defined. 78 | * 79 | * @param {ExpansionOptions} [expansionOptions] 80 | * For retrieving linked resources of the {@link Challenge} during this request. 81 | * 82 | * @param {Function} callback 83 | * The function to call when the operation is complete. Will be called with the 84 | * parameters (err, {@link Challenge}), or (err, null) if there is no most recent 85 | * challenge available. 86 | */ 87 | Factor.prototype.getMostRecentChallenge = function getMostRecentChallenge(/* [options], callback */) { 88 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 89 | 90 | if (!this.mostRecentChallenge || !this.mostRecentChallenge.href) { 91 | return process.nextTick(args.callback.bind(null, null, null)); 92 | } 93 | 94 | return this.dataStore.getResource(this.mostRecentChallenge.href, args.options, require('./Challenge'), args.callback); 95 | }; 96 | 97 | module.exports = Factor; 98 | -------------------------------------------------------------------------------- /lib/resource/FactorInstantiator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var Factor = require('./Factor'); 5 | var SmsFactor = require('./SmsFactor'); 6 | var GoogleAuthenticatorFactor = require('./GoogleAuthenticatorFactor'); 7 | var InstanceResource = require('./InstanceResource'); 8 | 9 | /** 10 | * Retrieves the constructor for a correct {@link Factor} instance ({@link SmsFactor} or 11 | * {@link GoogleAuthenticatorFactor}) for corresponding JSON data for the factor. 12 | * 13 | * @private 14 | * 15 | * @param {FactorData} factor 16 | * The data for the factor object 17 | * 18 | * @throws Error If the type is not defined or if it is not a valid type (SMS or google-authenticator) 19 | */ 20 | function getFactorConstructor(/* factor */) { 21 | var data = arguments[0]; 22 | 23 | if (!data || (typeof data.type === 'undefined')) { 24 | throw new Error('Factor instances must have a defined type'); 25 | } 26 | 27 | var type = data.type.toLowerCase(); 28 | 29 | switch (type) { 30 | case 'sms': 31 | return SmsFactor; 32 | case 'google-authenticator': 33 | return GoogleAuthenticatorFactor; 34 | default: 35 | return Factor; 36 | } 37 | } 38 | 39 | /** 40 | * @private 41 | * 42 | * @class FactorInstantiator 43 | * 44 | * @description 45 | * The constructor for {@link Factor} instances ({@link SmsFactor} or 46 | * {@link GoogleAuthenticatorFactor}). It parses the data to construct the 47 | * correct constructor, and calls it with the data. It is used for polymorphic 48 | * factor instantiation. It augments {@link InstanceResource} to adhere to the 49 | * interface used for instantiation in {@link ResourceFactory}. 50 | * 51 | * @augments InstanceResource 52 | */ 53 | function FactorInstantiator() { 54 | var Ctor = getFactorConstructor.apply(this, arguments); 55 | var argsWithContext = Array.prototype.slice.call(arguments); 56 | 57 | // Adds an initial parameter. It will be the function context (this) 58 | // for the bind call. It does not matter because `new` overwrites, it, 59 | // however, so we're just setting it to null here for syntactic reasons. 60 | argsWithContext.unshift(null); 61 | 62 | return new (Ctor.bind.apply(Ctor, argsWithContext))(); 63 | } 64 | 65 | utils.inherits(FactorInstantiator, InstanceResource); 66 | 67 | module.exports = { 68 | Constructor: FactorInstantiator, 69 | getConstructor: getFactorConstructor 70 | }; 71 | -------------------------------------------------------------------------------- /lib/resource/Field.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Resource = require('./Resource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class Field 8 | * 9 | * @description 10 | * Encapsulates an account field, as part of a {@link Schema}. 11 | * 12 | * This class should not be manually constructed. It should be obtained from one of these methods: 13 | * 14 | * - {@link Schema#getFields Schema.getFields()}. 15 | */ 16 | function Field(){ 17 | Field.super_.apply(this, arguments); 18 | } 19 | 20 | utils.inherits(Field, Resource); 21 | 22 | /** 23 | * Save changes to this resource. 24 | * 25 | * @param {Function} callback 26 | * The function to call when the save operation is complete. Will be called 27 | * with the parameters (err, updatedResource). 28 | */ 29 | Field.prototype.save = function save(callback) { 30 | this.dataStore.saveResource(this, callback); 31 | }; 32 | 33 | module.exports = Field; -------------------------------------------------------------------------------- /lib/resource/GoogleAuthenticatorFactor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Factor = require('./Factor'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class GoogleAuthenticatorFactor 8 | * 9 | * @description 10 | * 11 | * Encapsulates a Factor resource, used for purposes of Multi-Factor Authentication. 12 | * This type of Factor uses Google Authenticator as a multi-factor authentication method. 13 | * For full documentation of this resource, 14 | * please see 15 | * [REST API Reference: Creating a Factor](https://docs.stormpath.com/rest/product-guide/latest/reference.html#creating-a-factor). 16 | * 17 | * This class should not be manually constructed. It should be obtained from one 18 | * of these methods: 19 | * 20 | * - {@link Account#createFactor Account.createFactor()} 21 | * - {@link Account#getFactors Account.getFactors()} 22 | * - {@link Challenge#getFactor Challenge.getFactor()} 23 | * - {@link Client#getFactor Client.getFactor()} 24 | * 25 | * @augments {Factor} 26 | * 27 | * @param {Object} factorResource 28 | * 29 | * The JSON representation of this resource. 30 | */ 31 | function GoogleAuthenticatorFactor() { 32 | GoogleAuthenticatorFactor.super_.apply(this, arguments); 33 | } 34 | 35 | utils.inherits(GoogleAuthenticatorFactor, Factor); 36 | 37 | /** 38 | * Create a {@link Challenge} resource for this factor. The new challenge resource 39 | * will have a status of `CREATED`. Once the user provides a code from their Google 40 | * Authenticator application, you will verify it with `challenge.verifyCode()` 41 | * 42 | * @param {Object} [requestOptions] 43 | * Query parameters for this request. These can be any of the {@link ExpansionOptions}, 44 | * e.g. to retrieve linked resources of the new {@link Challenge} during this request. 45 | * 46 | * @param {Function} callback 47 | * The function to call when the operation is complete. Will be called with the 48 | * parameters (err, {@link Challenge}). 49 | * 50 | * @example 51 | * 52 | * googleAuthenticatorFactor.createChallenge(function(err, createdChallenge) { 53 | * if (err) { 54 | * return console.log(err) 55 | * } 56 | * 57 | * console.log(createdChallenge); 58 | * }); 59 | */ 60 | GoogleAuthenticatorFactor.prototype.createChallenge = function createFactorChallenge(/* callback */) { 61 | var args = utils.resolveArgs(arguments, ['options','callback'], true); 62 | return this.dataStore.createResource(this.challenges.href, args.options, null, require('./Challenge'), args.callback); 63 | }; 64 | 65 | module.exports = GoogleAuthenticatorFactor; 66 | -------------------------------------------------------------------------------- /lib/resource/GroupMembership.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class GroupMembership 8 | * 9 | * @description 10 | * Encapsulates a GroupMembership resource. For full documentation of this resource, please see 11 | * [REST API Reference: Group Membership](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#group-membership). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link Account#getGroupMemberships Account.getGroupMemberships()} 15 | * 16 | * To remove the group membership, call the `delete()` method on an instance of this class. 17 | * 18 | * @augments {InstanceResource} 19 | * 20 | * @param {Object} groupMembershipResource 21 | * The JSON representation of this resource, retrieved the Stormpath REST API. 22 | */ 23 | function GroupMembership() { 24 | GroupMembership.super_.apply(this, arguments); 25 | } 26 | utils.inherits(GroupMembership, InstanceResource); 27 | 28 | /** 29 | * Retrieves the {@link Account account} resource of this membership. 30 | * 31 | * @param {ExpansionOptions} [options] 32 | * For retrieving linked resources of the {@link Account} during this request. 33 | * 34 | * @param {Function} Callback 35 | * Callback function, will be called with (err, {@link Account account}). 36 | */ 37 | GroupMembership.prototype.getAccount = function getGroupMembershipAccount(/* [options,] callback */) { 38 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 39 | return this.dataStore.getResource(this.account.href, args.options, require('./Account'), args.callback); 40 | }; 41 | 42 | /** 43 | * Retrieves the {@link Group group} resource of this membership. 44 | * 45 | * @param {ExpansionOptions} [options] 46 | * For retrieving linked resources of the {@link Group group} during this request. 47 | * 48 | * @param {Function} Callback 49 | * Callback function, will be called with (err, {@link Group group}). 50 | */ 51 | GroupMembership.prototype.getGroup = function getGroupMembershipGroup(/* [options,] callback */) { 52 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 53 | return this.dataStore.getResource(this.group.href, args.options, require('./Group'), args.callback); 54 | }; 55 | 56 | module.exports = GroupMembership; 57 | -------------------------------------------------------------------------------- /lib/resource/IdSiteModel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class IdSiteModel 8 | * 9 | * @description 10 | * Encapsulates a IdSiteModel resource. For full documentation of this resource, please see 11 | * [REST API Reference: ID Site](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#id-site). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link Organization#getIdSiteModel Organization.getIdSiteModel()} 15 | * - {@link Tenant#getIdSites Tenant.getIdSites()} 16 | * 17 | * @augments {InstanceResource} 18 | * 19 | * @param {Object} idSiteModelResource 20 | * The JSON representation of this resource, retrieved the Stormpath REST API. 21 | */ 22 | function IdSiteModel() { 23 | IdSiteModel.super_.apply(this, arguments); 24 | } 25 | 26 | utils.inherits(IdSiteModel, InstanceResource); 27 | 28 | module.exports = IdSiteModel; 29 | -------------------------------------------------------------------------------- /lib/resource/OAuthPolicy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class OAuthPolicy 8 | * 9 | * @description 10 | * 11 | * Encapsulates a OAuth Policy resource of an {@link Application}. For full 12 | * documentation of this resource please see 13 | * [REST API Reference: OAuth Policy](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#oauth-policy). 14 | * 15 | * For a high level overview of this feature, please see 16 | * [How Token-Based Authentication Works](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#how-token-based-authentication-works). 17 | * 18 | * This class should not be manually constructed. It should be obtained from one 19 | * of these methods: 20 | * 21 | * - {@link Application#getOAuthPolicy Application.getOAuthPolicy()} 22 | * 23 | * @param {Object} oAuthPolicyResource 24 | * 25 | * The JSON representation of this resource, retrieved the Stormpath REST API. 26 | * 27 | */ 28 | function OAuthPolicy() { 29 | OAuthPolicy.super_.apply(this, arguments); 30 | } 31 | 32 | utils.inherits(OAuthPolicy, InstanceResource); 33 | 34 | module.exports = OAuthPolicy; 35 | 36 | 37 | /** 38 | * @method OAuthPolicy.save 39 | * 40 | * @description 41 | * 42 | * Save changes to this resource. 43 | * 44 | * @param {Function} callback 45 | * The function to call when the save operation is complete. Will be called 46 | * with the parameters (err, updatedResource). 47 | */ -------------------------------------------------------------------------------- /lib/resource/OrganizationAccountStoreMapping.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var AccountStoreMapping = require('./AccountStoreMapping'); 5 | 6 | /** 7 | * @class 8 | * 9 | * @augments {AccountStoreMapping} 10 | * 11 | * @description 12 | * 13 | * Encapsulates an Organization Account Store Mapping, which represents the link 14 | * between an {@link Organization} and a {@link Directory} or {@link Group}. For 15 | * full documentation of this resource, please see 16 | * [REST API Reference: Account Store Mapping](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#account-store-mapping). 17 | * 18 | * This class should not be manually constructed. It should be obtained from one 19 | * of these methods: 20 | * 21 | * - {@link Directory#getOrganizationMappings Directory.getOrganizationMappings()}. 22 | * - {@link Organization#getAccountStoreMappings Organization.getAccountStoreMappings()}. 23 | * 24 | * @param {object} accountStoreMappingResource 25 | * 26 | * The JSON representation of this resource. 27 | * 28 | */ 29 | function OrganizationAccountStoreMapping() { 30 | OrganizationAccountStoreMapping.super_.apply(this, arguments); 31 | } 32 | 33 | utils.inherits(OrganizationAccountStoreMapping, AccountStoreMapping); 34 | 35 | /** 36 | * @description 37 | * 38 | * Gets the {@link Organization} that is associated with this account store mapping. 39 | * 40 | * @param {ExpansionOptions} [expansionOptions] 41 | * For retrieving linked resources of the {@link Organization} during this request. 42 | * 43 | * @param {Function} callback 44 | * The callback to call when the operation is complete. Will be called with 45 | * (err, {@link Organization}). 46 | */ 47 | OrganizationAccountStoreMapping.prototype.getOrganization = function getOrganization(/* [options,] callback */) { 48 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 49 | return this.dataStore.getResource(this.organization.href, args.options, require('./Organization'), args.callback); 50 | }; 51 | 52 | /** 53 | * This is not necessary, organization can be passed to createAccountStoreMapping(). Remove in 1.0 54 | * 55 | * @private 56 | */ 57 | OrganizationAccountStoreMapping.prototype.setOrganization = function setOrganization(organization) { 58 | this.organization = { href: organization.href }; 59 | return this; 60 | }; 61 | 62 | module.exports = OrganizationAccountStoreMapping; 63 | -------------------------------------------------------------------------------- /lib/resource/PasswordPolicy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class PasswordPolicy 8 | * 9 | * @description 10 | * Encapsulates a PasswordPolicy resource of a {@link Directory}. For full documentation of this resource, please see 11 | * [REST API Reference: Password Policy](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#password-policy). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link Directory#getPasswordPolicy Directory.getPasswordPolicy()}. 15 | * 16 | * @augments {InstanceResource} 17 | * 18 | * @param {Object} passwordPolicyResource 19 | * The JSON representation of this resource, retrieved the Stormpath REST API. 20 | */ 21 | function PasswordPolicy() { 22 | PasswordPolicy.super_.apply(this, arguments); 23 | } 24 | 25 | utils.inherits(PasswordPolicy, InstanceResource); 26 | 27 | /** 28 | * Get the {@link Strength} resource of this PasswordPolicy resource. 29 | * 30 | * @param {ExpansionOptions} [expansionOptions] 31 | * For retrieving linked resources of the {@link Strength} resource during this request. 32 | * 33 | * @param {Function} callback 34 | * Callback function, will be called with (err, {@link Strength}). 35 | */ 36 | PasswordPolicy.prototype.getStrength = function() { 37 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 38 | return this.dataStore.getResource(this.strength.href, args.options, require('./Strength'), args.callback); 39 | }; 40 | 41 | module.exports = PasswordPolicy; 42 | -------------------------------------------------------------------------------- /lib/resource/PasswordResetToken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class PasswordResetToken 8 | * 9 | * @description 10 | * 11 | * Encapsulates a PasswordResetToken resource. For full documentation of this 12 | * resource, please see 13 | * [REST API Reference: Password Reset Tokens](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#password-reset-tokens). 14 | * 15 | * This class should not be manually constructed. It should be obtained from one 16 | * of these methods: 17 | * 18 | * - {@link Application#sendPasswordResetEmail Application.sendPasswordResetEmail()}. 19 | * 20 | * To revoke a password reset token, call `delete()` on an instance of this class. 21 | * 22 | * @param {Object} passwordResetTokenResource 23 | * 24 | * The JSON representation of this resource. 25 | * 26 | */ 27 | function PasswordResetToken() { 28 | PasswordResetToken.super_.apply(this, arguments); 29 | } 30 | utils.inherits(PasswordResetToken, InstanceResource); 31 | 32 | module.exports = PasswordResetToken; 33 | 34 | /** 35 | * @method PasswordResetToken.delete 36 | * 37 | * @description 38 | * 39 | * Deletes this resource from the API. 40 | * 41 | * @param {Function} callback 42 | * The function to call when the delete operation is complete. Will be called 43 | * with the parameter (err). 44 | */ 45 | -------------------------------------------------------------------------------- /lib/resource/Phone.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class Phone 8 | * 9 | * @description 10 | * 11 | * Encapsulates a Phone resource. For full documentation of this resource, 12 | * please see 13 | * [REST API Reference: Phone](https://docs.stormpath.com/rest/product-guide/latest/reference.html#ref-phone). 14 | * 15 | * This class should not be manually constructed. It should be obtained from one 16 | * of these methods: 17 | * 18 | * - {@link SmsFactor#getPhone SmsFactor.getPhone()} 19 | * 20 | * @augments {InstanceResource} 21 | * 22 | * @param {Object} phoneResource 23 | * 24 | * The JSON representation of this resource. 25 | */ 26 | function Phone() { 27 | Phone.super_.apply(this, arguments); 28 | } 29 | 30 | utils.inherits(Phone, InstanceResource); 31 | 32 | /** 33 | * Retrieves the {@link Account} this phone belongs to. 34 | * 35 | * @param {ExpansionOptions} [expansionOptions] 36 | * For retrieving linked resources of the {@link Account} for this request. 37 | * This resource supports expansions for `customData`, `tenant`, `directory`, 38 | * `groups`, and `groupMemberships`. Groups and group memberships can also be 39 | * expanded and paginated. 40 | * 41 | * @param {Function} callback 42 | * The function to call when the operation is complete. Will be called with 43 | * (err, {@link Account}). 44 | */ 45 | Phone.prototype.getAccount = function getPhoneAccount(/* callback */) { 46 | var args = utils.resolveArgs(arguments, ['callback'], true); 47 | return this.dataStore.getResource(this.account.href, {}, require('./Account'), args.callback); 48 | }; 49 | 50 | module.exports = Phone; 51 | -------------------------------------------------------------------------------- /lib/resource/Provider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class Provider 8 | * 9 | * @description 10 | * Encapsulates a Provider resource of a {@link Directory}. For full documentation of this resource, please see 11 | * [REST API Reference: Provider](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#provider). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link Directory#getProvider Directory.getProvider()}. 15 | * 16 | * @augments {InstanceResource} 17 | * 18 | * @param {Object} providerResource 19 | * The JSON representation of this resource, retrieved the Stormpath REST API. 20 | */ 21 | function Provider() { 22 | Provider.super_.apply(this, arguments); 23 | } 24 | 25 | utils.inherits(Provider, InstanceResource); 26 | 27 | module.exports = Provider; 28 | -------------------------------------------------------------------------------- /lib/resource/ProviderData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class ProviderData 8 | * 9 | * @description 10 | * Encapsulates a ProviderData resource of a {@link Account}. For full documentation of this resource, please see 11 | * [REST API Reference: ProviderData](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#provider-data). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link Account#getProviderData Account.getProviderData()}. 15 | * 16 | * @augments {InstanceResource} 17 | * 18 | * @param {Object} providerDataResource 19 | * The JSON representation of this resource, retrieved the Stormpath REST API. 20 | */ 21 | function ProviderData() { 22 | ProviderData.super_.apply(this, arguments); 23 | } 24 | 25 | utils.inherits(ProviderData, InstanceResource); 26 | 27 | module.exports = ProviderData; 28 | -------------------------------------------------------------------------------- /lib/resource/RefreshToken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | /** 6 | * @class RefreshToken 7 | * 8 | * @description 9 | * 10 | * Encapsulates a Stormpath OAuth Refresh Token Resource. For full documentation 11 | * of this resource, please see 12 | * [REST API Reference: Refresh Token](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#password-policy). 13 | * For a high-level overview of token management, please see 14 | * {@link https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#introduction-to-token-based-authentication Introduction to Token-Based Authentication}. 15 | * 16 | * This class should not be manually constructed. It should be obtained from one 17 | * of these methods: 18 | * 19 | * - {@link Client#getRefreshToken Client.getRefreshToken()} 20 | * - {@link Account#getRefreshTokens Account.getRefreshTokens()} 21 | * 22 | * To revoke a refresh token, invoke the `delete()` method on an instance of 23 | * this class. 24 | * 25 | * @param {Object} refreshTokenResource 26 | * 27 | * The JSON representation of this resource, retrieved the Stormpath REST API. 28 | */ 29 | function RefreshToken() { 30 | RefreshToken.super_.apply(this, arguments); 31 | } 32 | 33 | utils.inherits(RefreshToken, require('./InstanceResource')); 34 | 35 | module.exports = RefreshToken; 36 | 37 | /** 38 | * Deletes this resource from the API. 39 | * 40 | * @method RefreshToken.delete 41 | * 42 | * @param {Function} callback 43 | * The function to call when the delete operation is complete. Will be called 44 | * with the parameter (err). 45 | */ 46 | 47 | -------------------------------------------------------------------------------- /lib/resource/Resource.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | function Resource(data, dataStore) { 6 | // require moved here intentionally because of 7 | // issue related to 8 | var DataStore = require('../ds/DataStore'); 9 | if (!dataStore && data instanceof DataStore){ 10 | dataStore = data; 11 | data = null; 12 | } 13 | data = data || {}; 14 | 15 | for (var key in data) { 16 | if (data.hasOwnProperty(key)) { 17 | this[key] = data[key]; 18 | } 19 | } 20 | 21 | var ds = null; //private var, not enumerable 22 | Object.defineProperty(this, 'dataStore', { 23 | get: function getDataStore() { 24 | return ds; 25 | }, 26 | set: function setDataStore(dataStore) { 27 | ds = dataStore; 28 | } 29 | }); 30 | if (dataStore) { 31 | this.dataStore = dataStore; 32 | } 33 | } 34 | utils.inherits(Resource, Object); 35 | 36 | module.exports = Resource; -------------------------------------------------------------------------------- /lib/resource/SamlAttributeStatementMappingRules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class SamlAttributeStatementMappingRules 8 | * 9 | * @description 10 | * 11 | * Encapsulates a AttributeStatementMappingRules resource. 12 | * For more information about this resource, please see: 13 | * [REST API Reference: Configure SAML Attribute Mapping](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#step-7-configure-saml-attribute-mapping-optional). 14 | * 15 | * For a high-level overview of SAML Authentication, please see 16 | * [Authenticating Against a SAML Directory](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#authenticating-against-a-saml-directory). 17 | * 18 | * For more information about configuring SAML Authentication, please see 19 | * [Configuring SAML via REST](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#configuring-saml-via-rest). 20 | * 21 | * This class should not be manually constructed. It should be obtained from one 22 | * of these methods: 23 | * 24 | * - {@link SamlProvider#getAttributeStatementMappingRules SamlProvider.getAttributeStatementMappingRules()} 25 | * 26 | * @param {Object} attributeStatementMappingRules 27 | * 28 | * The JSON representation of this resource, retrieved the Stormpath REST API. 29 | * 30 | */ 31 | function SamlAttributeStatementMappingRules() { 32 | SamlAttributeStatementMappingRules.super_.apply(this, arguments); 33 | } 34 | 35 | utils.inherits(SamlAttributeStatementMappingRules, InstanceResource); 36 | 37 | module.exports = SamlAttributeStatementMappingRules; 38 | 39 | /** 40 | * @method SamlAttributeStatementMappingRules.save 41 | * 42 | * @description 43 | * 44 | * Save changes to this resource. 45 | * 46 | * @param {Function} callback 47 | * The function to call when the save operation is complete. Will be called 48 | * with the parameters (err, updatedResource). 49 | */ -------------------------------------------------------------------------------- /lib/resource/SamlPolicy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class SamlPolicy 8 | * 9 | * @description 10 | * 11 | * Encapsulates the SAML Policy of an {@link Application}. For full documentation 12 | * of the this resource, please see 13 | * [REST API Reference: SAML Policy Resource](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#saml-policy-resource). 14 | * 15 | * For a high-level overview of SAML Authentication, please see 16 | * [Authenticating Against a SAML Directory](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#authenticating-against-a-saml-directory). 17 | * 18 | * For more information about configuring SAML Authentication, please see 19 | * [Configuring SAML via REST](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#configuring-saml-via-rest). 20 | * 21 | * This class should not be manually constructed. It should be obtained from one 22 | * of these methods: 23 | * 24 | * - {@link Application#getSamlPolicy Application.getSamlPolicy()} 25 | * 26 | * @param {Object} samlPolicyResource 27 | * The JSON representation of this resource, retrieved the Stormpath REST API. 28 | * 29 | */ 30 | function SamlPolicy() { 31 | SamlPolicy.super_.apply(this, arguments); 32 | } 33 | 34 | utils.inherits(SamlPolicy, InstanceResource); 35 | 36 | /** 37 | * Get the {@link SamlServiceProvider} resource of this SAML Policy resource. 38 | * 39 | * @param {ExpansionOptions} [expansionOptions] 40 | * For retrieving linked resources of the {@link SamlServiceProvider} resource during this request. 41 | * 42 | * @param {Function} callback 43 | * Callback function, will be called with (err, {@link SamlServiceProvider samlServiceProvider}). 44 | */ 45 | SamlPolicy.prototype.getServiceProvider = function getServiceProvider(/* [options,] callback */) { 46 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 47 | return this.dataStore.getResource(this.serviceProvider.href, args.options, require('./SamlServiceProvider'), args.callback); 48 | }; 49 | 50 | module.exports = SamlPolicy; 51 | -------------------------------------------------------------------------------- /lib/resource/SamlProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class SamlProvider 8 | * 9 | * @description 10 | * 11 | * Encapsulates a SamlProvider resource, which is a type of {@link Provider}. 12 | * For full documentation of the Provider resource, please see 13 | * [REST API Reference: Provider](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#provider). 14 | * 15 | * For a high-level overview of SAML Authentication, please see 16 | * [Authenticating Against a SAML Directory](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#authenticating-against-a-saml-directory). 17 | * 18 | * For more information about configuring SAML Authentication, please see 19 | * [Configuring SAML via REST](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#configuring-saml-via-rest). 20 | * 21 | * This class should not be manually constructed. It should be obtained from one 22 | * of these methods: 23 | * 24 | * - {@link Directory#getProvider Directory.getProvider()} 25 | * 26 | * @augments {InstanceResource} 27 | * 28 | * @param {Object} samlProviderResource 29 | * 30 | * The JSON representation of this resource, retrieved the Stormpath REST API. 31 | * 32 | */ 33 | function SamlProvider() { 34 | SamlProvider.super_.apply(this, arguments); 35 | } 36 | 37 | utils.inherits(SamlProvider, InstanceResource); 38 | 39 | /** 40 | * Get the {@link SamlAttributeStatementMappingRules} resource of this SamlProvider resouce. 41 | * 42 | * @param {ExpansionOptions} [expansionOptions] 43 | * For retrieving linked resources of the {@link SamlAttributeStatementMappingRules} resource during this request. 44 | * 45 | * @param {Function} callback 46 | * Callback function, will be called with (err, {@link SamlAttributeStatementMappingRules samlServiceProviderMetadata}). 47 | */ 48 | SamlProvider.prototype.getAttributeStatementMappingRules = function getAttributeStatementMappingRules(/* [options,] callback */) { 49 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 50 | return this.dataStore.getResource(this.attributeStatementMappingRules.href, args.options, require('./SamlAttributeStatementMappingRules'), args.callback); 51 | }; 52 | 53 | /** 54 | * Get the {@link SamlServiceProviderMetadata} resource of this SamlProvider resouce. 55 | * 56 | * @param {ExpansionOptions} [expansionOptions] 57 | * For retrieving linked resources of the {@link SamlServiceProviderMetadata} resource during this request. 58 | * 59 | * @param {Function} callback 60 | * Callback function, will be called with (err, {@link SamlServiceProviderMetadata samlServiceProviderMetadata}). 61 | */ 62 | SamlProvider.prototype.getServiceProviderMetadata = function getServiceProviderMetadata(/* [options,] callback */) { 63 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 64 | return this.dataStore.getResource(this.serviceProviderMetadata.href, args.options, require('./SamlServiceProviderMetadata'), args.callback); 65 | }; 66 | 67 | module.exports = SamlProvider; 68 | -------------------------------------------------------------------------------- /lib/resource/SamlServiceProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class SamlServiceProvider 8 | * 9 | * @description 10 | * 11 | * Encapsulates the SAML Service Provider of an {@link Application}. For full 12 | * documentation of this resource, please see 13 | * [REST API Reference: SAML Policy Resource](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#saml-policy-resource). 14 | * 15 | * For a high-level overview of SAML Authentication, please see 16 | * [Authenticating Against a SAML Directory](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#authenticating-against-a-saml-directory). 17 | * 18 | * For more information about configuring SAML Authentication, please see 19 | * [Configuring SAML via REST](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#configuring-saml-via-rest). 20 | * 21 | * This class should not be manually constructed. It should be obtained from one 22 | * of these methods: 23 | * 24 | * - {@link SamlPolicy#getServiceProvider SamlPolicy.getServiceProvider()} 25 | * 26 | * @param {Object} samlServiceProviderResource 27 | * 28 | * The JSON representation of this resource, retrieved the Stormpath REST API. 29 | */ 30 | function SamlServiceProvider() { 31 | SamlServiceProvider.super_.apply(this, arguments); 32 | } 33 | 34 | utils.inherits(SamlServiceProvider, InstanceResource); 35 | 36 | module.exports = SamlServiceProvider; 37 | -------------------------------------------------------------------------------- /lib/resource/SamlServiceProviderMetadata.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class SamlServiceProviderMetadata 8 | * 9 | * @description 10 | * 11 | * Encapsulates a SAML Service Provider Metadata Resource. For full 12 | * documentation of this resource, please see 13 | * [Retrieve Your Service Provider Metadata](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#step-3-retrieve-your-service-provider-metadata). 14 | * 15 | * For a high-level overview of SAML Authentication, please see 16 | * [Authenticating Against a SAML Directory](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#authenticating-against-a-saml-directory). 17 | * 18 | * For more information about configuring SAML Authentication, please see 19 | * [Configuring SAML via REST](https://docs.stormpath.com/rest/product-guide/latest/auth_n.html?#configuring-saml-via-rest). 20 | * 21 | * This class should not be manually constructed. It should be obtained from one 22 | * of these methods: 23 | * 24 | * - {@link SamlProvider#getServiceProviderMetadata SamlProvider.getServiceProviderMetadata()} 25 | * 26 | * @param {Object} samlServiceProviderMetadataResource 27 | * 28 | * The JSON representation of this resource, retrieved the Stormpath REST API. 29 | */ 30 | function SamlServiceProviderMetadata() { 31 | SamlServiceProviderMetadata.super_.apply(this, arguments); 32 | } 33 | 34 | utils.inherits(SamlServiceProviderMetadata, InstanceResource); 35 | 36 | module.exports = SamlServiceProviderMetadata; 37 | -------------------------------------------------------------------------------- /lib/resource/Schema.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Resource = require('./Resource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class Schema 8 | * 9 | * @description 10 | * Encapsulates the Schema resource of a {@link Directory}. This schema allows you 11 | * to control which Account attributes (referred to as fields) are required when 12 | * creating new accounts in the directory. For full documentation of this resource, please see 13 | * [How to Manage an Account’s Required Attributes](https://docs.stormpath.com/rest/product-guide/latest/accnt_mgmt.html#how-to-manage-an-account-s-required-attributes). 14 | * 15 | * This class should not be manually constructed. It should be obtained from one of these methods: 16 | * - {@link Directory#getAccountSchema Directory.getAccountSchema()}. 17 | * 18 | * @example Disabling a field requirement. 19 | * var _ = require('lodash'); 20 | * 21 | * schema.getFields(function (err, fieldsCollection) { 22 | * var givenNameField = _.find(fieldsCollection.items, { 23 | * name: 'givenName' 24 | * }); 25 | * 26 | * givenNameField.required = false; 27 | * 28 | * givenNameField.save(); 29 | * }); 30 | */ 31 | function Schema() { 32 | Schema.super_.apply(this, arguments); 33 | } 34 | 35 | utils.inherits(Schema, Resource); 36 | 37 | /** 38 | * Get the collection of {@link Field Fields} for this schema. 39 | * 40 | * @param {CollectionQueryOptions} [options] 41 | * Options for querying, paginating, and expanding the collection. 42 | * 43 | * @param {Function} callback 44 | * The function to call when the operation is complete. Will be called 45 | * with the parameters (err, {@link CollectionResource}). The collection will 46 | * be a list of {@link Field} objects. 47 | */ 48 | Schema.prototype.getFields = function () { 49 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 50 | return this.dataStore.getResource(this.fields.href, args.options, require('./Field'), args.callback); 51 | }; 52 | 53 | module.exports = Schema; -------------------------------------------------------------------------------- /lib/resource/SmsFactor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Factor = require('./Factor'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class SmsFactor 8 | * 9 | * @description 10 | * 11 | * Encapsulates a Factor resource, used for purposes of Multi-Factor Authentication. 12 | * This type of Factor uses SMS as a multi-factor authentication method. 13 | * For full documentation of this resource, 14 | * please see 15 | * [REST API Reference: Creating a Factor](https://docs.stormpath.com/rest/product-guide/latest/reference.html#creating-a-factor). 16 | * 17 | * This class should not be manually constructed. It should be obtained from one 18 | * of these methods: 19 | * 20 | * - {@link Account#createFactor Account.createFactor()} 21 | * - {@link Account#getFactors Account.getFactors()} 22 | * - {@link Challenge#getFactor Challenge.getFactor()} 23 | * - {@link Client#getFactor Client.getFactor()} 24 | * 25 | * @augments {Factor} 26 | * 27 | * @param {Object} factorResource 28 | * 29 | * The JSON representation of this resource. 30 | */ 31 | function SmsFactor() { 32 | SmsFactor.super_.apply(this, arguments); 33 | } 34 | 35 | utils.inherits(SmsFactor, Factor); 36 | 37 | 38 | /** 39 | * Create a {@link Challenge} for this factor, which is used to start the multi-factor 40 | * authentication procedure. This will send an SMS message to the user with the 41 | * code. For more information about this process, See 42 | * {@link https://docs.stormpath.com/rest/product-guide/latest/auth_n.html#challenging-an-sms-factor Challenging an SMS Factor}. 43 | * 44 | * @param {Object} [challenge] 45 | * An object literal for configuring the challenge. If this object is not provied, 46 | * a default challenge message will be sent. 47 | * 48 | * @param {String} challenge.message 49 | * The message to be sent to the user. 50 | * 51 | * @param {Object} [requestOptions] 52 | * Query parameters for this request. These can be any of the {@link ExpansionOptions}, 53 | * e.g. to retrieve linked resources of the new {@link Challenge} during this request. 54 | * 55 | * @param {Function} callback 56 | * The function to call when the operation is complete. Will be called with the 57 | * parameters (err, {@link Challenge}). 58 | * 59 | * @example 60 | * var challenge = { 61 | * message: 'Your verification code is: ${code}' 62 | * }; 63 | * 64 | * smsFactor.createChallenge(challenge, function(err, createdChallenge) { 65 | * if (err) { 66 | * return console.log(err); 67 | * } 68 | * console.log(createdChallenge); 69 | * }); 70 | */ 71 | SmsFactor.prototype.createChallenge = function createFactorChallenge(/* challenge, [options], callback */) { 72 | var args = utils.resolveArgs(arguments, ['challenge', 'options', 'callback'], true); 73 | return this.dataStore.createResource(this.challenges.href, args.options, args.challenge, require('./Challenge'), args.callback); 74 | }; 75 | 76 | 77 | /** 78 | * Retrieves the phone to which the multi-factor authentication SMS is sent to. 79 | * 80 | * @param {ExpansionOptions} [expansionOptions] 81 | * For retrieving linked resources of the {@link Phone} during this request. 82 | * 83 | * @param {Function} callback 84 | * The function to call when the operation is complete. Will be called with the 85 | * parameters (err, {@link Phone}). 86 | */ 87 | SmsFactor.prototype.getPhone = function getPhone(/* callback */) { 88 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 89 | return this.dataStore.getResource(this.phone.href, args.options, require('./Phone'), args.callback); 90 | }; 91 | 92 | module.exports = SmsFactor; 93 | -------------------------------------------------------------------------------- /lib/resource/SmtpServer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | var InstanceResource = require('./InstanceResource'); 5 | 6 | /** 7 | * @class SmtpServer 8 | * 9 | * @description 10 | * Encapsulates a SmtpServer resource. For full documentation of this resource, please see 11 | * [REST API Reference: SMTP Server](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#smtp-server). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link Client#getSmtpServers Client.getSmtpServers()} 15 | * - {@link Tenant#getSmtpServers Tenant.getSmtpServers()} 16 | * 17 | * @augments {InstanceResource} 18 | * 19 | * @param {Object} smtpServerResource 20 | * The JSON representation of this resource, retrieved the Stormpath REST API. 21 | */ 22 | function SmtpServer() { 23 | SmtpServer.super_.apply(this, arguments); 24 | } 25 | 26 | utils.inherits(SmtpServer, InstanceResource); 27 | 28 | module.exports = SmtpServer; 29 | -------------------------------------------------------------------------------- /lib/resource/Strength.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var InstanceResource = require('./InstanceResource'); 4 | var utils = require('../utils'); 5 | 6 | /** 7 | * @class Strength 8 | * 9 | * @description 10 | * Encapsulates a Password Strength resource of a {@link PasswordPolicy}. For full documentation of the this resource, please see 11 | * [REST API Reference: Password Strength](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#password-strength). 12 | * 13 | * This class should not be manually constructed. It should be obtained from one of these methods: 14 | * - {@link PasswordPolicy#getStrength PasswordPolicy.getStrength()} 15 | * 16 | * @augments {InstanceResource} 17 | * 18 | * @param {Object} passwordStrengthResource 19 | * The JSON representation of this resource, retrieved the Stormpath REST API. 20 | */ 21 | function Strength() { 22 | Strength.super_.apply(this, arguments); 23 | } 24 | 25 | utils.inherits(Strength, InstanceResource); 26 | 27 | module.exports = Strength; 28 | -------------------------------------------------------------------------------- /lib/resource/mixins/SaveableMixin.js: -------------------------------------------------------------------------------- 1 | function applyCustomDataUpdatesIfNecessary(cb){ 2 | if (!this.customData){ 3 | return cb(); 4 | } 5 | 6 | if (this.customData._hasReservedFields()){ 7 | this.customData = this.customData._deleteReservedFields(); 8 | } 9 | 10 | if (this.customData._hasRemovedProperties()){ 11 | return this.customData._deleteRemovedProperties(cb); 12 | } 13 | 14 | return cb(); 15 | } 16 | 17 | /** 18 | * Save changes to this resource. 19 | * 20 | * @param {Function} callback 21 | * The function to call when the save operation is complete. Will be called 22 | * with the parameters (err, updatedResource). 23 | */ 24 | function saveResource(callback) { 25 | var self = this; 26 | self._applyCustomDataUpdatesIfNecessary(function () { 27 | self.dataStore.saveResource(self, callback); 28 | }); 29 | } 30 | 31 | var SaveableMixin = { 32 | _applyCustomDataUpdatesIfNecessary: applyCustomDataUpdatesIfNecessary, 33 | save: saveResource 34 | }; 35 | 36 | module.exports = SaveableMixin; 37 | -------------------------------------------------------------------------------- /lib/saml/SamlIdpUrlBuilder.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var njwt = require('njwt'); 4 | var uuid = require('uuid'); 5 | var querystring = require('querystring'); 6 | 7 | var utils = require('../utils'); 8 | 9 | /** 10 | * Creates a URL builder that can build SAML IDP redirect URLs. This is done 11 | * when Stormpath is initiating a redirect to a SAML IDP. 12 | * For more information, please see 13 | * [Authenticating Against a SAML Directory](http://docs.stormpath.com/rest/product-guide/latest/auth_n.html#authenticating-against-a-saml-directory). 14 | * 15 | * This authenticator is bound to the application that you pass to the constructor. 16 | * 17 | * @class 18 | * 19 | * @param {Application} application 20 | * 21 | * The Stormpath Application that will issue the redirect. This application must 22 | * be mapped to the relevant SAML Directories. 23 | * 24 | * @example 25 | * 26 | * var builder = new stormpath.SamlIdpUrlBuilder(application); 27 | */ 28 | function SamlIdpUrlBuilder(application) { 29 | this.application = application; 30 | this.apiKey = application.dataStore.requestExecutor.options.client.apiKey; 31 | } 32 | 33 | SamlIdpUrlBuilder.prototype._getServiceProvider = function _getServiceProvider(callback) { 34 | this.application.getSamlPolicy(function (err, samlPolicy) { 35 | if (err) { 36 | return callback(err); 37 | } 38 | 39 | samlPolicy.getServiceProvider(callback); 40 | }); 41 | }; 42 | 43 | SamlIdpUrlBuilder.prototype._buildInitializationUrl = function _buildInitializationUrl(initEndpointUrl, parameters) { 44 | return initEndpointUrl + '?' + querystring.stringify(parameters); 45 | }; 46 | 47 | /** 48 | * Builds a SAML IDP Redirect URL and provides it to the specified callback. 49 | * 50 | * @param {Object} options 51 | * Optional claims for the [SAML Authentication JWT](http://docs.stormpath.com/rest/product-guide/latest/auth_n.html#saml-authentication-jwt). 52 | * Use these claims to control the callback url (`cb_url`), token state (`state`), 53 | * and account store target (`ash` or `onk`). The other required claims will be 54 | * set automatically by the builder. 55 | * 56 | * @paarm {Function} callback 57 | * The callback to call with the URL string that was built. Will be called with 58 | * (err, urlString). 59 | * 60 | * @example 61 | * builder.build(function(err, url) { 62 | * if (err) { 63 | * console.error(err); 64 | * return; 65 | * } 66 | * console.log(url); 67 | * }); 68 | */ 69 | SamlIdpUrlBuilder.prototype.build = function (/* [options,] callback */) { 70 | var self = this; 71 | 72 | var apiKey = this.apiKey; 73 | var args = utils.resolveArgs(arguments, ['options', 'callback'], true); 74 | 75 | this._getServiceProvider(function (err, serviceProvider) { 76 | if (err) { 77 | return args.callback(err); 78 | } 79 | 80 | var claims = { 81 | jti: uuid(), 82 | iss: self.application.href, 83 | iat: new Date().getTime() / 1000 84 | }; 85 | 86 | var options = args.options || {}; 87 | 88 | if (options.cb_uri) { 89 | claims.cb_uri = options.cb_uri; 90 | } 91 | 92 | if (options.ash) { 93 | claims.ash = options.ash; 94 | } 95 | 96 | if (options.onk) { 97 | claims.onk = options.onk; 98 | } 99 | 100 | if (options.state) { 101 | claims.state = options.state; 102 | } 103 | 104 | var accessToken = njwt.create(claims, apiKey.secret); 105 | 106 | accessToken.header.kid = apiKey.id; 107 | 108 | var parameters = { 109 | accessToken: accessToken.compact() 110 | }; 111 | 112 | var ssoInitiationEndpoint = serviceProvider.ssoInitiationEndpoint.href; 113 | var initializationUrl = self._buildInitializationUrl(ssoInitiationEndpoint, parameters); 114 | 115 | args.callback(null, initializationUrl); 116 | }); 117 | }; 118 | 119 | module.exports = SamlIdpUrlBuilder; 120 | -------------------------------------------------------------------------------- /lib/stormpath.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var passwordGrant = require('./oauth/password-grant'); 4 | var refreshGrant = require('./oauth/refresh-grant'); 5 | var idSiteGrant = require('./oauth/id-site-grant'); 6 | var stormpathToken = require('./oauth/stormpath-token'); 7 | var clientCredentialsGrant = require('./oauth/client-credentials'); 8 | var stormpathSocial = require('./oauth/stormpath-social'); 9 | 10 | module.exports = { 11 | ApiKey: require('./authc/ApiKey'), 12 | loadApiKey: require('./authc/ApiKeyLoader'), 13 | configLoader: require('./configLoader'), 14 | Client: require('./Client'), 15 | Tenant: require('./resource/Tenant'), 16 | OAuthPasswordGrantRequestAuthenticator: passwordGrant.authenticator, 17 | OauthPasswordGrantAuthenticationResult: passwordGrant.authenticationResult, 18 | OAuthRefreshTokenGrantRequestAuthenticator: refreshGrant.authenticator, 19 | OAuthIdSiteTokenGrantAuthenticator: idSiteGrant.authenticator, 20 | OAuthIdSiteTokenGrantAuthenticationResult: idSiteGrant.authenticationResult, 21 | OAuthStormpathTokenAuthenticator: stormpathToken.authenticator, 22 | OAuthStormpathSocialAuthenticator: stormpathSocial.authenticator, 23 | OAuthStormpathTokenAuthenticationResult: stormpathToken.authenticationResult, 24 | OAuthClientCredentialsAuthenticator: clientCredentialsGrant.authenticator, 25 | OAuthClientCredentialsAuthenticationResult: clientCredentialsGrant.authenticationResult, 26 | SamlIdpUrlBuilder: require('./saml/SamlIdpUrlBuilder'), 27 | AssertionAuthenticationResult: require('./authc/AssertionAuthenticationResult'), 28 | StormpathAccessTokenAuthenticator: require('./oauth/stormpath-access-token-authenticator'), 29 | StormpathAssertionAuthenticator: require('./authc/StormpathAssertionAuthenticator'), 30 | JwtAuthenticator: require('./jwt/jwt-authenticator'), 31 | OAuthAuthenticator: require('./oauth/authenticator') 32 | }; 33 | -------------------------------------------------------------------------------- /lib/underscore.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('underscore'); 4 | 5 | // Import Underscore.string to separate object, because there are conflict functions (include, reverse, contains) 6 | _.str = require('underscore.string'); 7 | 8 | // Mix in non-conflict functions to Underscore namespace if you want 9 | _.mixin(_.str.exports()); 10 | 11 | // All functions, include conflict, will be available through _.str object 12 | _.str.include('Underscore.string', 'string'); // => true 13 | 14 | module.exports = _; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stormpath", 3 | "version": "0.20.1", 4 | "main": "lib/stormpath.js", 5 | "description": "Official Stormpath SDK for Node.js", 6 | "keywords": [ 7 | "stormpath", 8 | "api", 9 | "wrapper", 10 | "sdk", 11 | "client", 12 | "user", 13 | "user management", 14 | "user login", 15 | "identity", 16 | "identity management", 17 | "account", 18 | "account login", 19 | "login", 20 | "authentication", 21 | "authorization", 22 | "access control", 23 | "password", 24 | "password hash" 25 | ], 26 | "license": "Apache-2.0", 27 | "homepage": "https://github.com/stormpath/stormpath-sdk-node", 28 | "bugs": "https://github.com/stormpath/stormpath-sdk-node/issues", 29 | "author": { 30 | "name": "Stormpath, Inc.", 31 | "email": "support@stormpath.com", 32 | "url": "http://www.stormpath.com" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git://github.com/stormpath/stormpath-sdk-node.git" 37 | }, 38 | "scripts": { 39 | "coverage": "node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha", 40 | "docs": "./node_modules/.bin/jsdoc -c ./docs/jsdoc.json -d ./apidocs/ -P ./package.json -r lib/ --readme ./docs/JSDOC.md", 41 | "test": "grunt" 42 | }, 43 | "dependencies": { 44 | "async": "~1.5.2", 45 | "deep-extend": "^0.4.1", 46 | "jwt-simple": "~0.4.0", 47 | "memcached": "~2.2.2", 48 | "moment": "^2.15.2", 49 | "njwt": "^0.4.0", 50 | "properties-parser": "~0.3.1", 51 | "redis": "~2.6.2", 52 | "request": "~2.74.0", 53 | "stormpath-config": "0.0.27", 54 | "underscore": "~1.5.2", 55 | "underscore.string": "~3.2.3", 56 | "uuid": "^3.0.0", 57 | "xtend": "^4.0.1" 58 | }, 59 | "devDependencies": { 60 | "benchmark": "^2.0.0", 61 | "chai": "~3.5.0", 62 | "coveralls": "^2.11.11", 63 | "expand-home-dir": "0.0.3", 64 | "fake-fs": "^0.5.0", 65 | "grunt": "~1.0.1", 66 | "grunt-cli": "~1.2.0", 67 | "grunt-contrib-jshint": "~0.11.3", 68 | "grunt-contrib-watch": "~1.0.0", 69 | "grunt-mocha-istanbul": "~5.0.1", 70 | "grunt-mocha-test": "~0.12.7", 71 | "httpster": "^1.0.3", 72 | "ink-docstrap": "^1.1.4", 73 | "istanbul": "^0.4.4", 74 | "js-yaml": "~3.6.1", 75 | "jsdoc": "^3.4.0", 76 | "jshint-stylish": "~2.2.0", 77 | "load-grunt-tasks": "~3.5.0", 78 | "lodash": "^4.0.1", 79 | "mocha": "~2.5.3", 80 | "mocha-sinon": "~1.1.0", 81 | "nock": "~8.0.0", 82 | "nodemon": "^1.9.1", 83 | "sinon": "~1.17.3", 84 | "sinon-chai": "~2.8.0", 85 | "time-grunt": "~1.4.0", 86 | "timekeeper": "~0.1.1", 87 | "tmp": "0.0.28" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/apikey_test.js: -------------------------------------------------------------------------------- 1 | /*jshint expr: true*/ 2 | /*jshint unused: false*/ 3 | 'use strict'; 4 | 5 | var authc = require('../lib/authc'); 6 | var propsParser = require('properties-parser'); 7 | var tmp = require('tmp'); 8 | var fs = require('fs'); 9 | var chai = require('chai'); 10 | var should = chai.should(); 11 | 12 | chai.use(require('sinon-chai')); 13 | require('mocha-sinon'); 14 | 15 | var home = process.env[(process.platform === 'win32' ? 'USERPROFILE' : 'HOME')]; 16 | var apiKeyFilePath = home + '/.stormpath/apiKey.properties2'; 17 | 18 | describe('ApiKey', function () { 19 | var apiKey; 20 | before(function(done){ 21 | tmp.file(function _tempFileCreated(err, path, fd, cleanupCallback) { 22 | if(err){ throw err;} 23 | fs.write(fd, "apiKey.id = 1234\napiKey.secret = abcd"); 24 | fs.close(fd, function(err) { 25 | if(err){ throw err;} 26 | authc.loadApiKey(path,function(err,_apiKey){ 27 | if(err){ throw err;} 28 | apiKey = _apiKey; 29 | done(); 30 | }); 31 | }); 32 | }); 33 | }); 34 | it('should have id and secret properties', function () { 35 | apiKey.id.should.equal('1234'); 36 | apiKey.secret.should.equal('abcd'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require('lodash'); 4 | //_.extend(process.env, require('./test.env')); 5 | 6 | var chai = require("chai"); 7 | var nock = require('nock'); 8 | var sinon = require("sinon"); 9 | var assert = require('chai').assert; 10 | var should = require("chai").should(); 11 | var moment = require('moment'); 12 | var sinonChai = require("sinon-chai"); 13 | var uuid = require('uuid'); 14 | var nock = require('nock'); 15 | var timekeeper = require('timekeeper'); 16 | var jwt = require('njwt'); 17 | 18 | var Stormpath = require('../lib'); 19 | chai.use(sinonChai); 20 | 21 | function u(){} 22 | u.BASE_URL = 'https://api.stormpath.com'; 23 | /** adds '/v1' to relative URL, to work with nock request mocker */ 24 | u.v1 = function(s){return '/v1' + s;}; 25 | 26 | function random(){ 27 | return '' + Math.random()*Date.now(); 28 | } 29 | 30 | function clone(value) { 31 | return JSON.parse(JSON.stringify(value)); 32 | } 33 | 34 | function snapshotEnv() { 35 | var originalEnv = clone(process.env); 36 | return function restore() { 37 | var key; 38 | for (key in process.env) { 39 | if (!(key in originalEnv)) { 40 | delete process.env[key]; 41 | } 42 | } 43 | for (key in originalEnv) { 44 | process.env[key] = originalEnv[key]; 45 | } 46 | }; 47 | } 48 | 49 | function assertAccessTokenResponse(response){ 50 | assert.isDefined(response.accessTokenResponse); 51 | assert.isDefined(response.accessTokenResponse.access_token); 52 | assert.isDefined(response.accessTokenResponse.refresh_token); 53 | assert.isDefined(response.accessToken); 54 | assert.isDefined(response.refreshToken); 55 | } 56 | 57 | function assertPasswordGrantResponse(done){ 58 | return function(err,response){ 59 | assert.isNull(err); 60 | assert.instanceOf(response,Stormpath.OauthPasswordGrantAuthenticationResult); 61 | assertAccessTokenResponse(response); 62 | done(); 63 | }; 64 | } 65 | 66 | module.exports = { 67 | _: _, 68 | u: u, 69 | chai: chai, 70 | nock: nock, 71 | sinon: sinon, 72 | assert: assert, 73 | expect: chai.expect, 74 | config: process.env, 75 | should: should, 76 | moment: moment, 77 | Stormpath: Stormpath, 78 | timekeeper: timekeeper, 79 | random: random, 80 | uuid: uuid, 81 | jwt: jwt, 82 | snapshotEnv: snapshotEnv, 83 | assertPasswordGrantResponse: assertPasswordGrantResponse, 84 | assertAccessTokenResponse: assertAccessTokenResponse 85 | }; 86 | -------------------------------------------------------------------------------- /test/fixtures/account-token.js: -------------------------------------------------------------------------------- 1 | /* 2 | This fixture creates a new application, directory, 3 | and account. It issues an Oauth request for the 4 | account, resulting in an access token and refresh 5 | token in the token collections for the account 6 | */ 7 | 8 | var helpers = require('../it/helpers'); 9 | var OAuthPasswordGrantRequestAuthenticator = require('../../').OAuthPasswordGrantRequestAuthenticator; 10 | 11 | function AccountAccessTokenFixture(){ 12 | } 13 | 14 | AccountAccessTokenFixture.prototype.before = function before(done) { 15 | var self = this; 16 | helpers.getClient(function(_client){ 17 | 18 | self.client = _client; 19 | helpers.createApplication(function(err,app){ 20 | if (err) { 21 | return done(err); 22 | } 23 | 24 | self.application = app; 25 | helpers.getDefaultAccountStore(app,function(err,dir){ 26 | if (err) { 27 | return done(err); 28 | } 29 | 30 | self.directory = dir; 31 | self.newAccount = helpers.fakeAccount(); 32 | dir.createAccount(self.newAccount, function(err,_account) { 33 | self.account = _account; 34 | self.creationResult = [err, _account]; 35 | 36 | /* 37 | We need to do a password grant request, so that we create 38 | an access token and refresh token for this user 39 | */ 40 | 41 | var authenticator = new OAuthPasswordGrantRequestAuthenticator(app); 42 | authenticator.authenticate({ 43 | username: _account.username, 44 | password: self.newAccount.password 45 | },function(err,passwordGrantResult){ 46 | if(err){ 47 | done(err); 48 | }else{ 49 | self.passwordGrantResult = passwordGrantResult; 50 | done(); 51 | } 52 | }); 53 | }); 54 | 55 | }); 56 | 57 | 58 | }); 59 | }); 60 | }; 61 | AccountAccessTokenFixture.prototype.after = function after(done) { 62 | helpers.cleanupApplicationAndStores(this.application, done); 63 | }; 64 | 65 | module.exports = AccountAccessTokenFixture; 66 | -------------------------------------------------------------------------------- /test/fixtures/apiKey.properties: -------------------------------------------------------------------------------- 1 | apiKey.id = api_key_id 2 | apiKey.secret = api_key_secret 3 | -------------------------------------------------------------------------------- /test/it/assertion_authentication_result_it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('../common'); 4 | var DataStore = require('../../lib/ds/DataStore'); 5 | 6 | var assert = common.assert; 7 | var sinon = common.sinon; 8 | var stormpath = common.Stormpath; 9 | 10 | var AssertionAuthenticationResult = stormpath.AssertionAuthenticationResult; 11 | 12 | describe('AssertionAuthenticationResult', function () { 13 | 14 | var authenticationResult; 15 | var sandbox; 16 | var dataStore; 17 | 18 | var mockAccount = { 19 | href: 'http://stormpath.mock/api/v1/account/123', 20 | name: 'foo' 21 | }; 22 | 23 | before(function () { 24 | 25 | dataStore = new DataStore({ 26 | client: { 27 | apiKey: { 28 | id: 'abc', 29 | secret: '123' 30 | } 31 | } 32 | }); 33 | 34 | sandbox = sinon.sandbox.create(); 35 | 36 | var data = { 37 | account: { 38 | href: mockAccount.href 39 | }, 40 | dataStore: { 41 | foo: 'this dataStore propety should be ignored, as it will already be defined by the first dataStore parmater' 42 | } 43 | }; 44 | 45 | authenticationResult = new AssertionAuthenticationResult(dataStore, data); 46 | 47 | sandbox.stub(dataStore, 'getResource', function (href, data, callback) { 48 | callback(null, mockAccount); 49 | }); 50 | }); 51 | 52 | after(function () { 53 | sandbox.restore(); 54 | }); 55 | 56 | describe('when constructed', function () { 57 | 58 | it('should apply the data store parmater to the object', function(){ 59 | assert.equal(authenticationResult.dataStore, dataStore); 60 | }); 61 | 62 | it('should apply the data parmater properties to the object', function(){ 63 | assert.ok(authenticationResult.account); 64 | assert.equal(authenticationResult.account.href, mockAccount.href); 65 | }); 66 | }); 67 | 68 | describe('.getAccount()', function () { 69 | 70 | it('should error if no account is defined', function(done) { 71 | new AssertionAuthenticationResult({},{}).getAccount(function(err){ 72 | assert.isOk(err); 73 | assert.equal(err.message, 'Unable to get account. Account HREF not specified.'); 74 | done(); 75 | }); 76 | }); 77 | 78 | it('should call the callback with the account result', function (done) { 79 | authenticationResult.getAccount(function (err, result) { 80 | assert.notOk(err); 81 | assert.deepEqual(result, mockAccount); 82 | done(); 83 | }); 84 | }); 85 | }); 86 | }); -------------------------------------------------------------------------------- /test/it/datastore_it.js: -------------------------------------------------------------------------------- 1 | 2 | var common = require('../common'); 3 | var helpers = require('./helpers'); 4 | var assert = common.assert; 5 | 6 | describe('DataStore', function() { 7 | describe('when asked for a resource', function() { 8 | var cacheResult, resource; 9 | 10 | before(function(done) { 11 | helpers.getClient(function(client) { 12 | client.getCurrentTenant(function(err, tenant) { 13 | assert.ifError(err); 14 | resource = tenant; 15 | 16 | client._dataStore.cacheHandler.get(tenant.href, function(err, value) { 17 | assert.ifError(err); 18 | cacheResult = value; 19 | done(); 20 | }); 21 | }); 22 | }); 23 | }); 24 | 25 | it('should cache the resource', function() { 26 | assert.equal(cacheResult.href, resource.href); 27 | }); 28 | }); 29 | 30 | describe('when asked for a collection',function(){ 31 | var cacheResult; 32 | 33 | before(function(done){ 34 | helpers.getClient(function(client){ 35 | client.getCurrentTenant(function(err,tenant){ 36 | if(err){throw err;} 37 | tenant.getApplications(function(err,collection){ 38 | if(err){throw err;} 39 | client._dataStore.cacheHandler.get(collection.href,function(err,value){ 40 | if(err){throw err;} 41 | cacheResult = value; 42 | done(); 43 | }); 44 | }); 45 | }); 46 | }); 47 | }); 48 | 49 | it('should not cache the collection',function(){ 50 | assert.equal(cacheResult,null); 51 | }); 52 | }); 53 | 54 | describe('when asked for a previously created resource w/ expansions,',function(){ 55 | var directory, expandedDirectory; 56 | before(function(done){ 57 | helpers.getClient(function(client){ 58 | 59 | client.createDirectory(helpers.fakeDirectory(),function(err,_directory){ 60 | if(err){throw err;} 61 | directory = _directory; 62 | client.getDirectory(directory.href,{expand:'customData'},function(err,_directory){ 63 | if(err){throw err;} 64 | expandedDirectory = _directory; 65 | done(); 66 | }); 67 | }); 68 | }); 69 | }); 70 | after(function(done){ 71 | directory.delete(done); 72 | }); 73 | it('should return the resource w/ expansions',function(){ 74 | assert.equal(typeof expandedDirectory.customData.createdAt,'string'); 75 | }); 76 | 77 | }); 78 | 79 | }); 80 | -------------------------------------------------------------------------------- /test/it/oauth_id_site_token_grant_it.js: -------------------------------------------------------------------------------- 1 | var common = require('../common'); 2 | var helpers = require('./helpers'); 3 | var assert = common.assert; 4 | 5 | var stormpath = require('../../'); 6 | 7 | describe('OAuthIdSiteTokenGrantAuthenticator',function(){ 8 | var application; 9 | var newAccount; 10 | 11 | before(function(done){ 12 | newAccount = helpers.fakeAccount(); 13 | 14 | helpers.createApplication(function(err,app){ 15 | application = app; 16 | application.createAccount(newAccount,done); 17 | }); 18 | }); 19 | 20 | after(function(done){ 21 | helpers.cleanupApplicationAndStores(application, done); 22 | }); 23 | 24 | it('should be constructable with new operator',function(){ 25 | var authenticator = new stormpath.OAuthIdSiteTokenGrantAuthenticator(application); 26 | assert.instanceOf(authenticator, stormpath.OAuthIdSiteTokenGrantAuthenticator); 27 | }); 28 | 29 | it('should be constructable without new operator',function(){ 30 | var authenticator = stormpath.OAuthIdSiteTokenGrantAuthenticator(application); 31 | assert.instanceOf(authenticator, stormpath.OAuthIdSiteTokenGrantAuthenticator); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/it/oauth_refresh_grant_it.js: -------------------------------------------------------------------------------- 1 | 2 | var common = require('../common'); 3 | var helpers = require('./helpers'); 4 | var assert = common.assert; 5 | 6 | var stormpath = require('../../'); 7 | 8 | describe('OAuthRefreshTokenGrantRequestAuthenticator',function(){ 9 | 10 | var application, refreshToken; 11 | 12 | var newAccount; 13 | 14 | before(function(done){ 15 | newAccount = helpers.fakeAccount(); 16 | 17 | helpers.createApplication(function(err,app){ 18 | if(err){ 19 | done(err); 20 | }else{ 21 | application = app; 22 | application.createAccount(newAccount,function(err){ 23 | if(err){ 24 | done(err); 25 | }else{ 26 | var authenticator = new stormpath.OAuthPasswordGrantRequestAuthenticator(application); 27 | authenticator.authenticate({ 28 | username: newAccount.username, 29 | password: newAccount.password 30 | },function(err,passwordGrantResult){ 31 | if(err){ 32 | done(err); 33 | }else{ 34 | refreshToken = passwordGrantResult.refreshToken; 35 | done(); 36 | } 37 | }); 38 | } 39 | }); 40 | } 41 | 42 | }); 43 | }); 44 | 45 | after(function(done){ 46 | helpers.cleanupApplicationAndStores(application, done); 47 | }); 48 | 49 | it('should be constructable with new operator',function(){ 50 | var authenticator = new stormpath.OAuthRefreshTokenGrantRequestAuthenticator(application); 51 | assert.instanceOf(authenticator,stormpath.OAuthRefreshTokenGrantRequestAuthenticator); 52 | }); 53 | 54 | it('should be constructable without new operator',function(){ 55 | var authenticator = stormpath.OAuthRefreshTokenGrantRequestAuthenticator(application); 56 | assert.instanceOf(authenticator,stormpath.OAuthRefreshTokenGrantRequestAuthenticator); 57 | }); 58 | 59 | it('should refresh access tokens',function(done){ 60 | var authenticator = new stormpath.OAuthRefreshTokenGrantRequestAuthenticator(application); 61 | authenticator.authenticate({ 62 | refresh_token: refreshToken.toString() 63 | },function(err,response){ 64 | common.assertAccessTokenResponse(response); 65 | done(); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/it/organization_it.js: -------------------------------------------------------------------------------- 1 | 2 | var common = require('../common'); 3 | var helpers = require('./helpers'); 4 | var assert = common.assert; 5 | var async = require('async'); 6 | 7 | var Organization = require('../../lib/resource/Organization'); 8 | var OrganizationAccountStoreMapping = require('../../lib/resource/OrganizationAccountStoreMapping'); 9 | 10 | describe('Organization',function(){ 11 | 12 | var client, organization, directory, mapping; 13 | 14 | before(function(done) { 15 | helpers.getClient(function(_client) { 16 | client = _client; 17 | var org = { 18 | name: helpers.uniqId(), 19 | nameKey: helpers.uniqId() 20 | }; 21 | var dir = { 22 | name: helpers.uniqId() 23 | }; 24 | client.createOrganization(org, function(err, _organization) { 25 | if(err){ throw err; } 26 | organization = _organization; 27 | 28 | client.createDirectory(dir,function(err,_directory){ 29 | if(err){ throw err; } 30 | directory = _directory; 31 | done(); 32 | }); 33 | 34 | 35 | }); 36 | }); 37 | }); 38 | 39 | after(function(done) { 40 | async.eachSeries([mapping,directory, organization ], function(resource, next) { 41 | resource.delete(next); 42 | }, done); 43 | }); 44 | 45 | describe('createAccountStoreMapping',function(){ 46 | after(function(done){ 47 | mapping.delete(done); 48 | }); 49 | it('should create an OrganizationAccountStoreMapping',function(done){ 50 | organization.createAccountStoreMapping({accountStore:directory},function(err,_mapping){ 51 | mapping = _mapping; 52 | assert(_mapping instanceof OrganizationAccountStoreMapping); 53 | done(); 54 | }); 55 | }); 56 | it('should handle errors',function(done){ 57 | organization.createAccountStoreMapping({accountStore:{href:'not found'}},function(err){ 58 | assert(err.status === 400); 59 | assert(err.code === 2002); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | 65 | describe('getOrganization',function(){ 66 | after(function(done){ 67 | mapping.delete(done); 68 | }); 69 | it('should return the organization',function(done){ 70 | mapping.getOrganization(function(err,organization){ 71 | assert(organization instanceof Organization); 72 | done(); 73 | }); 74 | }); 75 | }); 76 | 77 | describe('createAccountStoreMappings',function(){ 78 | it('should create an OrganizationAccountStoreMapping',function(done){ 79 | organization.createAccountStoreMappings([{accountStore:directory}],function(err,results){ 80 | if(err){ 81 | done(err); 82 | }else{ 83 | assert(results[0] instanceof OrganizationAccountStoreMapping); 84 | mapping = results[0]; 85 | done(); 86 | } 87 | }); 88 | }); 89 | }); 90 | 91 | describe('getAccountStoreMappings',function(){ 92 | it('should get an OrganizationAccountStoreMapping',function(done){ 93 | organization.getAccountStoreMappings(function(err,collection){ 94 | assert(collection.items[0] instanceof OrganizationAccountStoreMapping); 95 | done(); 96 | }); 97 | }); 98 | }); 99 | 100 | }); 101 | -------------------------------------------------------------------------------- /test/it/password_policy_it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var uuid = require('uuid'); 5 | var Client = require('../../lib/Client'); 6 | 7 | describe('PasswordPolicy', function() { 8 | describe('getStrength', function() { 9 | var client, directory; 10 | 11 | before(function (done) { 12 | client = new Client(); 13 | 14 | client.on('error', function (err) { 15 | done(err); 16 | }); 17 | 18 | client.on('ready', function () { 19 | done(); 20 | }); 21 | }); 22 | 23 | after(function (done) { 24 | directory.delete(function(err) { 25 | done(err); 26 | }); 27 | }); 28 | 29 | it('should return the strength object', function (done) { 30 | client.createDirectory({ name: uuid.v4() }, function (err, dir) { 31 | if (err) { 32 | return done(err); 33 | } 34 | 35 | directory = dir; 36 | 37 | directory.getPasswordPolicy(function (err, policy) { 38 | if (err) { 39 | return done(err); 40 | } 41 | 42 | policy.getStrength(function (err, strength) { 43 | if (err) { 44 | return done(err); 45 | } 46 | 47 | assert(strength.href); 48 | done(); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/it/saml_idp_url_builder_it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var nJwt = require('njwt'); 4 | var url = require('url'); 5 | var helpers = require('./helpers'); 6 | var common = require('../common'); 7 | 8 | var assert = common.assert; 9 | var stormpath = common.Stormpath; 10 | 11 | var SamlIdpUrlBuilder = stormpath.SamlIdpUrlBuilder; 12 | 13 | describe('SamlIdpUrlBuilder', function () { 14 | var builder, application; 15 | 16 | before(function (done) { 17 | application = helpers.createApplication(function (err, app) { 18 | if (err) { 19 | return done(err); 20 | } 21 | 22 | application = app; 23 | builder = new SamlIdpUrlBuilder(app); 24 | 25 | done(); 26 | }); 27 | }); 28 | 29 | after(function(done){ 30 | helpers.cleanupApplicationAndStores(application, done); 31 | }); 32 | 33 | describe('.build()', function () { 34 | describe('without options', function () { 35 | it('should return a valid url', function (done) { 36 | builder.build(function (err, resultUrl) { 37 | assert.isNotOk(err); 38 | assert.isOk(resultUrl); 39 | 40 | var parsedUrl = url.parse(resultUrl, true); 41 | 42 | assert.ok(parsedUrl); 43 | assert.equal(parsedUrl.pathname, url.parse(application.href).pathname + '/saml/sso/idpRedirect'); 44 | assert.isDefined(parsedUrl.query.accessToken); 45 | 46 | done(); 47 | }); 48 | }); 49 | }); 50 | describe('with options', function () { 51 | it('should encode those options in the accessToken', function (done) { 52 | var options = { 53 | cb_uri: 'http://mysite.com/samlCallback', 54 | onk: 'my-org', 55 | ash: 'https://api.stormpath.com/v1/directories/:dirId', 56 | state: 'hello' 57 | }; 58 | builder.build(options, function (err, resultUrl) { 59 | assert.isNull(err); 60 | assert.isOk(resultUrl); 61 | 62 | var parsedUrl = url.parse(resultUrl, true); 63 | 64 | var secret = application.dataStore.requestExecutor.options.client.apiKey.secret; 65 | 66 | assert.isDefined(parsedUrl.query.accessToken); 67 | 68 | var jwt = nJwt.verify(parsedUrl.query.accessToken, secret); 69 | 70 | assert.equal(jwt.body.cb_uri, options.cb_uri); 71 | assert.equal(jwt.body.onsk, options.onsk); 72 | assert.equal(jwt.body.ash, options.ash); 73 | assert.equal(jwt.body.state, options.state); 74 | 75 | done(); 76 | }); 77 | }); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/sp.authc.apiKeyLoader_test.js: -------------------------------------------------------------------------------- 1 | /* jshint -W030 */ 2 | 'use strict'; 3 | 4 | var common = require('./common'); 5 | var expect = common.expect; 6 | 7 | var ApiKey = require('../lib/authc/ApiKey'); 8 | var loadApiKey = require('../lib/authc/ApiKeyLoader'); 9 | 10 | var path = './test/fixtures/apiKey.properties'; 11 | 12 | describe('authc', function () { 13 | describe('ApiKey loader', function(){ 14 | it('should load prefixed file path with file://', function(done){ 15 | loadApiKey('file://' + path, function(err, apiKey){ 16 | expect(err).to.be.null; 17 | apiKey.should.be.ok; 18 | done(); 19 | }); 20 | }); 21 | it('should load preferences with relative file path', function(done){ 22 | loadApiKey(path, function(err, apiKey){ 23 | expect(err).to.be.null; 24 | apiKey.should.be.ok; 25 | done(); 26 | }); 27 | }); 28 | it('should return error in callback if path is wrong', function(done){ 29 | loadApiKey('boom' + path, function(err, apiKey){ 30 | err.should.be.an.instanceof(Error); 31 | err.message.should.match(/unable to read/i); 32 | expect(apiKey).to.be.null; 33 | done(); 34 | }); 35 | }); 36 | it('should return instance of ApiKey with loaded params', function(done){ 37 | loadApiKey(path, function(err, apiKey){ 38 | apiKey.should.be.an.instanceof(ApiKey); 39 | apiKey.id.should.be.equal('api_key_id'); 40 | apiKey.secret.should.be.equal('api_key_secret'); 41 | done(); 42 | }); 43 | }); 44 | }); 45 | }); -------------------------------------------------------------------------------- /test/sp.authc.apiKey_test.js: -------------------------------------------------------------------------------- 1 | /* jshint -W030 */ 2 | 'use strict'; 3 | 4 | var ApiKey = require('../lib/authc/ApiKey'); 5 | 6 | describe('authc', function () { 7 | describe('ApiKey class', function(){ 8 | var id; 9 | var secret; 10 | var apiKey; 11 | 12 | before(function () { 13 | id = 'id'; 14 | secret = 'boom!'; 15 | apiKey = new ApiKey(id, secret); 16 | }); 17 | 18 | it('should expose id and secret as fields', function(){ 19 | apiKey.id.should.be.equal(id); 20 | apiKey.secret.should.be.equal(secret); 21 | }); 22 | it('should hide secret', function(){ 23 | apiKey.toString().should.contain(id); 24 | apiKey.toString().should.not.contain(secret); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/sp.authc.authRequestParser_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | 5 | var AuthRequestParser = require('../lib/authc/AuthRequestParser'); 6 | 7 | describe('AuthRequestParser', function() { 8 | it('should throw an error if request is not an object', function() { 9 | assert.throws(function() { 10 | new AuthRequestParser('test', 'woo'); 11 | }, Error); 12 | }); 13 | 14 | it('should throw an error if request.url is not a string', function() { 15 | assert.throws(function() { 16 | new AuthRequestParser({ 17 | body: null, 18 | headers: {}, 19 | method: 'get', 20 | url: 1, 21 | }, ['body']); 22 | }, Error); 23 | }); 24 | 25 | it('should throw an error if locationsToSearch is not an array', function() { 26 | assert.throws(function() { 27 | new AuthRequestParser({ 28 | body: null, 29 | headers: {}, 30 | method: 'get', 31 | url: 'test' 32 | }, 'woo'); 33 | }, Error); 34 | }); 35 | 36 | it('should throw an error if request.method is not a string', function() { 37 | assert.throws(function() { 38 | new AuthRequestParser({ method: 1 }, {}); 39 | }, Error); 40 | }); 41 | 42 | it('should throw an error if request.headers is not an object', function() { 43 | assert.throws(function() { 44 | new AuthRequestParser({ headers: 'test', method: 'get' }, {}); 45 | }, Error); 46 | }); 47 | 48 | it('should allow the request.body to be null', function() { 49 | var parser = new AuthRequestParser({ 50 | body: null, 51 | headers: {}, 52 | method: 'get', 53 | url: 'test' 54 | }, ['body']); 55 | 56 | assert.equal(Object.keys(parser.body).length, 0); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/sp.authc.requestAuthenticator_test.js: -------------------------------------------------------------------------------- 1 | /* jshint -W030 */ 2 | 'use strict'; 3 | 4 | var RequestAuthenticator = require('../lib/authc/RequestAuthenticator'); 5 | 6 | describe('authc', function () { 7 | describe('RequestAuthenticator class', function () { 8 | function createAuth(apiKey){ 9 | return function(){ 10 | return new RequestAuthenticator(apiKey); 11 | }; 12 | } 13 | describe('if apiKey not provided', function () { 14 | it('should throw api key is required exception', function () { 15 | createAuth(null).should.throw(/apiKey is required/i); 16 | }); 17 | }); 18 | 19 | describe('if apiKey.id not provided', function () { 20 | it('should throw apiKey.id is required', function () { 21 | createAuth({}).should.throw(/apiKey.id is required/); 22 | }); 23 | }); 24 | describe('if apiKey.secret not provided', function () { 25 | it('should throw apiKey.secret is required', function () { 26 | createAuth({id: 1}).should.throw(/apiKey.secret is required/); 27 | }); 28 | }); 29 | }); 30 | }); -------------------------------------------------------------------------------- /test/sp.cache.disabledCache_test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var _ = common._; 3 | var should = common.should; 4 | 5 | describe('Cache module',function(){ 6 | 7 | describe('DisabledCache cache stub', function(){ 8 | var DisabledCache = require('../lib/cache/DisabledCache'); 9 | var disabledCache = new DisabledCache(); 10 | function callToMethod(cache, methodName){ 11 | describe('call to',function(){ 12 | it(methodName+ ' method should return null, null', function(done){ 13 | cache[methodName](function(err, res){ 14 | should.not.exist(err); 15 | should.not.exist(res); 16 | done(); 17 | }); 18 | }); 19 | }); 20 | } 21 | _.each(disabledCache.prototype, function(method, methodName){ 22 | callToMethod(disabledCache,methodName); 23 | }); 24 | }); 25 | }); -------------------------------------------------------------------------------- /test/sp.cache.manager_test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var should = common.should; 3 | var expect = common.expect; 4 | 5 | var CacheManager = require('../lib/cache/CacheManager'); 6 | 7 | describe('Cache module',function(){ 8 | 9 | describe('Cache Manager class', function(){ 10 | describe('By default', function(){ 11 | var manager; 12 | 13 | before(function () { 14 | manager = new CacheManager(); 15 | }); 16 | 17 | it('cache should be empty', function(){ 18 | /* jshint -W030 */ 19 | manager.caches.should.deep.equal({}); 20 | }); 21 | it('stats should be empty', function(){ 22 | /* jshint -W030 */ 23 | expect(manager.stats).deep.equal({}); 24 | }); 25 | }); 26 | 27 | describe('create cache',function(){ 28 | var manager; 29 | var region; 30 | 31 | before(function(){ 32 | manager = new CacheManager(); 33 | region = common.uuid(); 34 | 35 | manager.createCache(region); 36 | }); 37 | it('should create cache instance', function(){ 38 | should.exist(manager.getCache(region)); 39 | }); 40 | it('should add stats', function (){ 41 | should.exist(manager.stats); 42 | }); 43 | }); 44 | 45 | describe('get cache',function(){ 46 | var manager; 47 | var region; 48 | 49 | before(function(){ 50 | manager = new CacheManager(); 51 | region = common.uuid(); 52 | 53 | manager.createCache(region); 54 | }); 55 | it('should return cache instance', function(){ 56 | should.exist(manager.getCache(region)); 57 | }); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/sp.cache.memoryStore_test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var should = common.should; 3 | 4 | var MemoryStore = require('../lib/cache/MemoryStore'); 5 | 6 | describe('Cache module', function () { 7 | 8 | describe('In memory cache store', function () { 9 | var memoryStore; 10 | 11 | before(function () { 12 | memoryStore = new MemoryStore(); 13 | }); 14 | 15 | describe('set entry', function () { 16 | var key; 17 | var val; 18 | var entry; 19 | 20 | before(function (done) { 21 | key = 'key' + Date.now(); 22 | val = 'val' + Date.now(); 23 | 24 | memoryStore.set(key, val, function () { 25 | memoryStore.get(key, function (err, ent) { 26 | entry = ent; 27 | done(); 28 | }); 29 | }); 30 | }); 31 | after(function (done) { 32 | memoryStore.delete(key, done); 33 | }); 34 | it('should store value', function () { 35 | should.exist(entry); 36 | }); 37 | it('stored value should be accessible', function () { 38 | entry.should.be.equal(val); 39 | }); 40 | }); 41 | 42 | describe('get entry', function () { 43 | var key; 44 | var val; 45 | 46 | before(function (done) { 47 | key = 'key' + Date.now(); 48 | val = 'val' + Date.now(); 49 | 50 | memoryStore.set(key, val, done); 51 | }); 52 | after(function (done) { 53 | memoryStore.delete(key, done); 54 | }); 55 | 56 | it('should return entry if found', function (done) { 57 | memoryStore.get(key, function (err, entry) { 58 | should.not.exist(err); 59 | should.exist(entry); 60 | entry.should.be.equal(val); 61 | done(); 62 | }); 63 | }); 64 | it('should return null if not found', function (done) { 65 | memoryStore.get(Date.now(), function (err, entry) { 66 | should.not.exist(err); 67 | should.not.exist(entry); 68 | done(); 69 | }); 70 | }); 71 | }); 72 | 73 | describe('delete entry', function () { 74 | var key; 75 | var val; 76 | 77 | before(function (done) { 78 | key = 'key' + Date.now(); 79 | val = 'val' + Date.now(); 80 | 81 | memoryStore.set(key, val, done); 82 | }); 83 | it('should remove entry from store', function (done) { 84 | memoryStore.delete(key, function () { 85 | memoryStore.get(key, function (err, entry) { 86 | should.not.exist(entry); 87 | done(); 88 | }); 89 | }); 90 | }); 91 | }); 92 | 93 | describe('clear cache', function () { 94 | var key; 95 | var val; 96 | 97 | before(function (done) { 98 | key = 'key' + Date.now(); 99 | val = 'val' + Date.now(); 100 | 101 | memoryStore.set(key, val, done); 102 | }); 103 | it('should remove all entries from store', function (done) { 104 | memoryStore.clear(function () { 105 | memoryStore.get(key, function (err, entry) { 106 | should.not.exist(entry); 107 | done(); 108 | }); 109 | }); 110 | }); 111 | }); 112 | 113 | describe('cache size', function () { 114 | var key; 115 | var val; 116 | 117 | before(function (done) { 118 | key = 'key' + Date.now(); 119 | val = 'val' + Date.now(); 120 | 121 | memoryStore.clear(function(){ 122 | memoryStore.set(key, val, done); 123 | }); 124 | }); 125 | it('should return store size', function (done) { 126 | memoryStore.size(function (err, size) { 127 | size.should.be.equal(1); 128 | done(); 129 | }); 130 | }); 131 | }); 132 | }); 133 | }); 134 | -------------------------------------------------------------------------------- /test/sp.cache.stats_test.js: -------------------------------------------------------------------------------- 1 | var CacheStats = require('../lib/cache/CacheStats'); 2 | 3 | describe('Cache module',function(){ 4 | 5 | // make sens only for single instance!!! 6 | describe('Cache Stats class', function(){ 7 | var stats; 8 | 9 | before(function () { 10 | stats = new CacheStats(); 11 | }); 12 | 13 | describe('if we put a new entry', function(){ 14 | var putsCounter, sizeCounter; 15 | before(function(){ 16 | putsCounter = stats.puts; 17 | sizeCounter = stats.size; 18 | stats.put(true); 19 | }); 20 | it('should increase puts counter', function(){ 21 | stats.puts.should.be.equal(putsCounter + 1); 22 | }); 23 | it('should increase size counter', function(){ 24 | stats.size.should.be.equal(sizeCounter + 1); 25 | }); 26 | }); 27 | 28 | describe('if we put an old entry', function(){ 29 | var putsCounter, sizeCounter; 30 | before(function(){ 31 | putsCounter = stats.puts; 32 | sizeCounter = stats.size; 33 | 34 | stats.put(false); 35 | }); 36 | it('should increase puts counter', function(){ 37 | stats.puts.should.be.equal(putsCounter + 1); 38 | }); 39 | it('should not increase size counter', function(){ 40 | stats.size.should.be.equal(sizeCounter); 41 | }); 42 | }); 43 | 44 | describe('if we hit an entry', function(){ 45 | var hitsCounter; 46 | before(function(){ 47 | hitsCounter = stats.hits; 48 | 49 | stats.hit(); 50 | }); 51 | it('should increase hits counter', function(){ 52 | stats.hits.should.be.equal(hitsCounter + 1); 53 | }); 54 | }); 55 | 56 | describe('if we miss a new entry', function(){ 57 | var missesCounter,expirationsCounter; 58 | before(function(){ 59 | missesCounter = stats.misses; 60 | expirationsCounter = stats.expirations; 61 | 62 | stats.miss(true); 63 | }); 64 | 65 | it('should increase misses counter',function(){ 66 | stats.misses.should.be.equal(missesCounter + 1); 67 | }); 68 | it('should increase expirations counter', function(){ 69 | stats.expirations.should.be.equal(expirationsCounter + 1); 70 | }); 71 | }); 72 | 73 | describe('if we miss an expired entry', function(){ 74 | var missesCounter,expirationsCounter; 75 | before(function(){ 76 | missesCounter = stats.misses; 77 | expirationsCounter = stats.expirations; 78 | 79 | stats.miss(false); 80 | }); 81 | it('should increase misses counter', function(){ 82 | stats.misses.should.be.equal(missesCounter + 1); 83 | }); 84 | it('should not increase expirations counter', function(){ 85 | stats.expirations.should.be.equal(expirationsCounter); 86 | }); 87 | }); 88 | 89 | describe('if we delete an entry from cache', function(){ 90 | var sizeCounter; 91 | before(function(){ 92 | sizeCounter = stats.size; 93 | 94 | stats.delete(); 95 | }); 96 | it('should decrease size counter', function(){ 97 | stats.size.should.be.equal(sizeCounter - 1); 98 | }); 99 | }); 100 | 101 | describe('if we delete an entry from empty cache', function(){ 102 | before(function(){ 103 | stats.clear(); 104 | stats.delete(); 105 | }); 106 | it('should not decrease size counter', function(){ 107 | stats.size.should.be.equal(0); 108 | }); 109 | }); 110 | 111 | describe('if we clear cache', function(){ 112 | before(function(){ 113 | stats.clear(); 114 | }); 115 | it('should reset size counter', function(){ 116 | stats.size.should.be.equal(0); 117 | }); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/sp.error.resourceError_test.js: -------------------------------------------------------------------------------- 1 | var ResourceError = require('../lib/error/ResourceError'); 2 | 3 | describe('Error:', function () { 4 | describe('Resource Error', function () { 5 | var response; 6 | var re; 7 | 8 | before(function () { 9 | response = { 10 | status: 400, 11 | code: 100500, 12 | message: 'hi user', 13 | developerMessage: 'hi dev', 14 | moreInfo: 'boom!' 15 | }; 16 | 17 | re = new ResourceError(response); 18 | }); 19 | 20 | it('should inherit from error', function () { 21 | re.should.be.an.instanceof(Error); 22 | }); 23 | 24 | it('should dispose response fields', function () { 25 | re.name.should.match(/ResourceError/i); 26 | re.status.should.be.equal(response.status); 27 | re.code.should.be.equal(response.code); 28 | re.userMessage.should.be.equal(response.message); 29 | re.developerMessage.should.be.equal(response.developerMessage); 30 | re.moreInfo.should.be.equal(response.moreInfo); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/sp.resource.accountLink_test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var assert = common.assert; 3 | var sinon = common.sinon; 4 | 5 | var Account = require('../lib/resource/Account'); 6 | var AccountLink = require('../lib/resource/AccountLink'); 7 | var DataStore = require('../lib/ds/DataStore'); 8 | 9 | describe('Resources: ', function() { 10 | describe('AccountLink resource class', function() { 11 | var dataStore; 12 | 13 | before(function () { 14 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 15 | }); 16 | 17 | describe('Constructor', function() { 18 | it('should inherit from InstanceResource', function() { 19 | AccountLink.super_.name.should.equal('InstanceResource'); 20 | }); 21 | }); 22 | 23 | describe('methods', function() { 24 | var sandbox; 25 | var opts; 26 | var accountLinkData; 27 | var accountLink; 28 | var getResourceStub; 29 | var cbSpy; 30 | 31 | before(function() { 32 | sandbox = sinon.sandbox.create(); 33 | 34 | opts = { 35 | boom: 'of course' 36 | }; 37 | 38 | accountLinkData = { 39 | href: 'boom!', 40 | createdAt: '', 41 | modifiedAt: '', 42 | leftAccount: { 43 | href: 'leftBoom!' 44 | }, 45 | rightAccount: { 46 | href: 'rightBoom!' 47 | } 48 | }; 49 | 50 | accountLink = new AccountLink(accountLinkData, dataStore); 51 | 52 | getResourceStub = sandbox.stub(dataStore, 'getResource', function(href, options, ctor, cb) { 53 | cb(); 54 | }); 55 | 56 | cbSpy = sandbox.spy(); 57 | }); 58 | 59 | after(function() { 60 | sandbox.restore(); 61 | }); 62 | 63 | describe('#getLeftAccount()', function() { 64 | before(function() { 65 | accountLink.getLeftAccount(cbSpy); 66 | accountLink.getLeftAccount(opts, cbSpy); 67 | }); 68 | 69 | it('should get the leftAccount resource', function() { 70 | /* jshint -W030 */ 71 | getResourceStub.should.have.been.calledTwice; 72 | cbSpy.should.have.been.calledTwice; 73 | /* jshint +W030 */ 74 | 75 | getResourceStub.should.have.been.calledWith( 76 | 'leftBoom!', 77 | null, 78 | Account, 79 | cbSpy 80 | ); 81 | 82 | getResourceStub.should.have.been.calledWith( 83 | 'leftBoom!', 84 | opts, 85 | Account, 86 | cbSpy 87 | ); 88 | }); 89 | }); 90 | 91 | describe('#getRightAccount()', function() { 92 | before(function() { 93 | accountLink.getRightAccount(cbSpy); 94 | accountLink.getRightAccount(opts, cbSpy); 95 | }); 96 | 97 | it('should get the rightAccount resource', function() { 98 | assert.equal(getResourceStub.callCount, 4); 99 | assert.equal(cbSpy.callCount, 4); 100 | 101 | getResourceStub.should.have.been.calledWith( 102 | 'rightBoom!', 103 | null, 104 | Account, 105 | cbSpy 106 | ); 107 | 108 | getResourceStub.should.have.been.calledWith( 109 | 'rightBoom!', 110 | opts, 111 | Account, 112 | cbSpy 113 | ); 114 | }); 115 | }); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /test/sp.resource.accountLinkingPolicy_test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var assert = common.assert; 3 | var sinon = common.sinon; 4 | 5 | var AccountLinkingPolicy = require('../lib/resource/AccountLinkingPolicy'); 6 | var Tenant = require('../lib/resource/Tenant'); 7 | var DataStore = require('../lib/ds/DataStore'); 8 | 9 | describe('Resources: ', function() { 10 | describe('AccountLinkingPolicy resource class', function() { 11 | var dataStore; 12 | 13 | before(function() { 14 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 15 | }); 16 | 17 | describe('Constructor', function() { 18 | it('should inherit from InstanceResource', function() { 19 | AccountLinkingPolicy.super_.name.should.equal('InstanceResource'); 20 | }); 21 | 22 | it('should *not* inherit the delete method', function() { 23 | var alp = new AccountLinkingPolicy({href: 'boom!'}, dataStore); 24 | assert.isUndefined(alp.delete); 25 | }); 26 | }); 27 | 28 | describe('methods', function() { 29 | var sandbox; 30 | var opts; 31 | var alpData; 32 | var alp; 33 | var getResourceStub; 34 | var cbSpy; 35 | 36 | before(function() { 37 | sandbox = sinon.sandbox.create(); 38 | 39 | opts = { 40 | boom: 'of course' 41 | }; 42 | 43 | alpData = { 44 | href: 'boom!', 45 | createdAt: '', 46 | modifiedAt: '', 47 | status: 'ENABLED', 48 | automaticProvisioning: 'ENABLED', 49 | matchingProperty: 'email', 50 | tenant: { 51 | href: 'boom!' 52 | } 53 | }; 54 | 55 | alp = new AccountLinkingPolicy(alpData, dataStore); 56 | 57 | getResourceStub = sandbox.stub(dataStore, 'getResource', function(href, options, ctor, cb) { 58 | cb(); 59 | }); 60 | 61 | cbSpy = sandbox.spy(); 62 | }); 63 | 64 | after(function() { 65 | sandbox.restore(); 66 | }); 67 | 68 | describe('#getTenant()', function() { 69 | before(function() { 70 | alp.getTenant(cbSpy); 71 | alp.getTenant(opts, cbSpy); 72 | }); 73 | 74 | it('should get the leftAccount resource', function() { 75 | /* jshint -W030 */ 76 | getResourceStub.should.have.been.calledTwice; 77 | cbSpy.should.have.been.calledTwice; 78 | /* jshint +W030 */ 79 | 80 | getResourceStub.should.have.been.calledWith( 81 | 'boom!', 82 | null, 83 | Tenant, 84 | cbSpy 85 | ); 86 | 87 | getResourceStub.should.have.been.calledWith( 88 | 'boom!', 89 | opts, 90 | Tenant, 91 | cbSpy 92 | ); 93 | }); 94 | }); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/sp.resource.apikey_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var sinon = require('sinon'); 5 | var uuid = require('uuid'); 6 | 7 | var ApiKey = require('../lib/resource/ApiKey'); 8 | var DataStore = require('../lib/ds/DataStore'); 9 | 10 | describe('resource', function() { 11 | describe('ApiKey', function() { 12 | var clientApiKeySecret, sandbox, cbSpy, apiKey, getResourceStub; 13 | 14 | before(function(done) { 15 | clientApiKeySecret = uuid(); 16 | sandbox = sinon.sandbox.create(); 17 | cbSpy = sandbox.spy(); 18 | 19 | apiKey = new ApiKey({ 20 | id: 'id', 21 | secret: 'secret', 22 | account: { 23 | href: '/boom' 24 | } 25 | }); 26 | 27 | apiKey.dataStore = new DataStore({ 28 | client: { 29 | apiKey: { 30 | id: '1', 31 | secret: clientApiKeySecret 32 | } 33 | } 34 | }); 35 | 36 | getResourceStub = sinon.stub(apiKey.dataStore, 'getResource', function() { 37 | var args = Array.prototype.slice.call(arguments); 38 | var href = args.shift(); 39 | var callback = args.pop(); 40 | callback(null, {href: href}); 41 | }); 42 | 43 | done(); 44 | }); 45 | 46 | after(function() { 47 | sandbox.restore(); 48 | }); 49 | 50 | it('should provide a getAccount method', function() { 51 | assert(typeof apiKey.getAccount === 'function'); 52 | }); 53 | 54 | describe('getAccount', function() { 55 | it('should return an Account object', function(done) { 56 | apiKey.getAccount(function(err, account) { 57 | if (err) { 58 | return done(err); 59 | } 60 | 61 | assert(account.href === '/boom'); 62 | assert(getResourceStub.calledOnce); 63 | 64 | done(); 65 | }); 66 | }); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/sp.resource.authenticationResult_test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var common = require('./common'); 4 | var sinon = common.sinon; 5 | var assert = common.assert; 6 | var timekeeper = common.timekeeper; 7 | 8 | var Account = require('../lib/resource/Account'); 9 | var AuthenticationResult = require('../lib/resource/AuthenticationResult'); 10 | var DataStore = require('../lib/ds/DataStore'); 11 | 12 | describe('Resources: ', function () { 13 | describe('Authentication Result resource', function () { 14 | var dataStore; 15 | 16 | before(function () { 17 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 18 | }); 19 | 20 | describe('if ttl', function () { 21 | describe('isn\'t set', function () { 22 | it('should have a default value of 3600', function () { 23 | var result = new AuthenticationResult(); 24 | assert.equal(result.ttl, 3600); 25 | }); 26 | }); 27 | 28 | describe('is set', function () { 29 | var app; 30 | var result; 31 | 32 | before(function () { 33 | app = {account: {href: 'boom!'}, dataStore: dataStore}; 34 | result = new AuthenticationResult(app, dataStore); 35 | 36 | result.application = app; 37 | result.ttl = 9999; 38 | }); 39 | 40 | it('should return jwt with specified ttl', function () { 41 | timekeeper.freeze(0); 42 | 43 | var jwt = result.getJwt(); 44 | assert.equal(jwt.body.exp, new Date().getTime() + result.ttl); 45 | 46 | timekeeper.reset(); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('get accounts', function () { 52 | describe('if accounts not set', function () { 53 | //var authcResult = new AuthenticationResult(); 54 | 55 | //function getAccountsWithoutHref() { 56 | // authcResult.getAccount(); 57 | //} 58 | 59 | //it('should throw unhandled exception', function () { 60 | // getAccountsWithoutHref.should 61 | // .throw(/cannot read property 'href' of undefined/i); 62 | //}); 63 | }); 64 | 65 | describe('if accounts are set', function () { 66 | var sandbox, authcResult, getResourceStub, cbSpy, app, opt; 67 | before(function () { 68 | sandbox = sinon.sandbox.create(); 69 | app = {account: {href: 'boom!'}}; 70 | opt = {}; 71 | authcResult = new AuthenticationResult(app, dataStore); 72 | getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) { 73 | cb(); 74 | }); 75 | cbSpy = sandbox.spy(); 76 | 77 | // call without optional param 78 | authcResult.getAccount(cbSpy); 79 | // call with optional param 80 | authcResult.getAccount(opt, cbSpy); 81 | }); 82 | after(function () { 83 | sandbox.restore(); 84 | }); 85 | 86 | it('should get accounts', function () { 87 | /* jshint -W030 */ 88 | getResourceStub.should.have.been.calledTwice; 89 | cbSpy.should.have.been.calledTwice; 90 | /* jshint +W030 */ 91 | 92 | // call without optional param 93 | getResourceStub.should.have.been 94 | .calledWith(app.account.href, null, Account, cbSpy); 95 | // call with optional param 96 | getResourceStub.should.have.been 97 | .calledWith(app.account.href, opt, Account, cbSpy); 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/sp.resource.factorInstantiator_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var assert = common.assert; 5 | var sinon = common.sinon; 6 | 7 | var InstanceResource = require('../lib/resource/InstanceResource'); 8 | var FactorInstantiator = require('../lib/resource/FactorInstantiator'); 9 | var FactorConstructor = FactorInstantiator.Constructor; 10 | var Factor = require('../lib/resource/Factor'); 11 | var SmsFactor = require('../lib/resource/SmsFactor'); 12 | var GoogleAuthenticatorFactor = require('../lib/resource/GoogleAuthenticatorFactor'); 13 | 14 | describe('FactorInstantiator#Constructor', function() { 15 | var sandbox; 16 | 17 | before(function() { 18 | sandbox = sinon.sandbox.create(); 19 | }); 20 | 21 | after(function() { 22 | sandbox.restore(); 23 | }); 24 | 25 | it('should inherit from InstanceResource', function() { 26 | assert.equal(FactorConstructor.super_, InstanceResource); 27 | }); 28 | 29 | it('should construct an SmsFactor if type is SMS', function() { 30 | assert.instanceOf(new FactorConstructor({type: 'SMS'}), SmsFactor); 31 | }); 32 | 33 | it('should construct a GoogleAuthenticatorFactor if type is google-authenticator', function() { 34 | assert.instanceOf(new FactorConstructor({type: 'google-authenticator'}), GoogleAuthenticatorFactor); 35 | }); 36 | }); 37 | 38 | describe('FactorInstantiator#getConstructor', function() { 39 | var sandbox; 40 | var getConstructor; 41 | 42 | before(function() { 43 | sandbox = sinon.sandbox.create(); 44 | getConstructor = FactorInstantiator.getConstructor; 45 | }); 46 | 47 | after(function() { 48 | sandbox.restore(); 49 | }); 50 | 51 | it('should throw an error if called without any parameters', function() { 52 | assert.throws(getConstructor, Error, 'Factor instances must have a defined type'); 53 | }); 54 | 55 | it('should throw an error if called without a type parameter', function() { 56 | assert.throws(getConstructor.bind(null, {}), Error, 'Factor instances must have a defined type'); 57 | }); 58 | 59 | it('should return the base Factor type if called with an unknown type parameter', function() { 60 | assert.equal(getConstructor({type: 'foo'}), Factor); 61 | }); 62 | 63 | it('should not throw an error if called with a valid type (sms or google-authenticator)', function() { 64 | assert.doesNotThrow(getConstructor.bind(null, {type: 'SMS'}), Error); 65 | assert.doesNotThrow(getConstructor.bind(null, {type: 'google-authenticator'}), Error); 66 | }); 67 | 68 | it('should construct an SmsFactor if the type is `SMS`, regardless of capitalization', function() { 69 | assert.equal(getConstructor({type: 'sms'}), SmsFactor); 70 | assert.equal(getConstructor({type: 'SMS'}), SmsFactor); 71 | assert.equal(getConstructor({type: 'sMs'}), SmsFactor); 72 | }); 73 | 74 | it('should construct an GoogleAuthenticatorFactor if the type is `google-authenticator`, regardless of capitalization', function() { 75 | assert.equal(getConstructor({type: 'google-authenticator'}), GoogleAuthenticatorFactor); 76 | assert.equal(getConstructor({type: 'GOOGLE-AUTHENTICATOR'}), GoogleAuthenticatorFactor); 77 | assert.equal(getConstructor({type: 'Google-Authenticator'}), GoogleAuthenticatorFactor); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/sp.resource.field_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var sinon = common.sinon; 5 | 6 | var DataStore = require('../lib/ds/DataStore'); 7 | var Field = require('../lib/resource/Field'); 8 | 9 | describe('Field Resource', function () { 10 | describe('save()', function () { 11 | var sandbox; 12 | var field; 13 | var dataStore; 14 | var requestExecutorStub; 15 | 16 | var mockField = { 17 | href: 'https://api.stormpath.com/v1/fields/7dDfMOkrekkLhbWBLcGWuN', 18 | createdAt: '2016-08-02T20:16:21.931Z', 19 | modifiedAt: '2016-08-02T20:16:21.931Z', 20 | name: 'givenName', 21 | required: true, 22 | schema: { 23 | href: 'https://api.stormpath.com/v1/schemas/7dDfMLQmkARN4mQK9MPGIJ' 24 | } 25 | }; 26 | 27 | before(function () { 28 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 29 | sandbox = sinon.sandbox.create(); 30 | field = new Field(mockField, dataStore); 31 | requestExecutorStub = sandbox.stub(dataStore.requestExecutor, 'execute'); 32 | }); 33 | 34 | after(function () { 35 | sandbox.restore(); 36 | }); 37 | 38 | it('should post the resource to the REST API', function () { 39 | field.save(); 40 | requestExecutorStub.should.have.been.calledWith({ 41 | body: mockField, 42 | uri: mockField.href, 43 | method: 'POST' 44 | }); 45 | }); 46 | }); 47 | }); -------------------------------------------------------------------------------- /test/sp.resource.googleAuthenticatorFactor_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var assert = common.assert; 5 | var sinon = common.sinon; 6 | 7 | var Factor = require('../lib/resource/Factor'); 8 | var GoogleAuthenticatorFactor = require('../lib/resource/GoogleAuthenticatorFactor'); 9 | var DataStore = require('../lib/ds/DataStore'); 10 | var Challenge = require('../lib/resource/Challenge'); 11 | 12 | var factorData = { 13 | href: 'https://api.stormpath.com/v1/factors/wzU29J38OcAyY1z8TeX1x', 14 | type: 'google-authenticator', 15 | createdAt: '2016-09-22T17:58:09.645Z', 16 | modifiedAt: '2016-09-22T17:58:09.646Z', 17 | status: 'ENABLED', 18 | verificationStatus: 'UNVERIFIED', 19 | account: { 20 | href: 'https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spexample' 21 | }, 22 | challenges: { 23 | href: 'https://api.stormpath.com/v1/factors/wzU29J38OcAyY1z8TeX1x/challenges' 24 | }, 25 | mostRecentChallenge: { 26 | href: 'https://api.stormpath.com/v1/challenges/wzYMCbEUJ5Nx4S7VRSMkX' 27 | } 28 | }; 29 | 30 | describe('GoogleAuthenticatorFactor', function() { 31 | var dataStore; 32 | var factor; 33 | var superSpy; 34 | var createResourceStub; 35 | 36 | before(function() { 37 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 38 | superSpy = sinon.spy(GoogleAuthenticatorFactor, 'super_'); 39 | factor = new GoogleAuthenticatorFactor(factorData, dataStore); 40 | createResourceStub = sinon.stub(dataStore, 'createResource'); 41 | }); 42 | 43 | it('should inherit from Factor', function() { 44 | assert.instanceOf(factor, Factor); 45 | }); 46 | 47 | it('should call super_ with the same parameters', function() { 48 | superSpy.should.have.been.calledWithExactly(factorData, dataStore); 49 | }); 50 | 51 | describe('GoogleAuthenticatorFactor#createChallenge', function() { 52 | var callback; 53 | var options = {}; 54 | 55 | before(function() { 56 | callback = sinon.spy(); 57 | 58 | factor.createChallenge(options, callback); 59 | }); 60 | 61 | it('should call DataStore#createResource', function() { 62 | /*jshint -W030 */ 63 | createResourceStub.should.have.been.calledOnce; 64 | /*jshint +W030 */ 65 | }); 66 | 67 | it('should pass the correct href to DataStore#createResource', function() { 68 | createResourceStub.args[0][0].should.equal(factorData.challenges.href); 69 | }); 70 | 71 | it('should pass options to DataStore#createResource', function() { 72 | assert.equal(createResourceStub.args[0][1], options); 73 | }); 74 | 75 | it('should not pass a request body to DataStore#createResource', function() { 76 | assert.isNull(createResourceStub.args[0][2]); 77 | }); 78 | 79 | it('should pass the correct constructor to DataStore#createResource', function() { 80 | createResourceStub.args[0][3].should.equal(Challenge); 81 | }); 82 | 83 | it('should pass the correct callback to DataStore#createResource', function() { 84 | createResourceStub.args[0][4].should.equal(callback); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/sp.resource.phone_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var assert = common.assert; 5 | var sinon = common.sinon; 6 | 7 | var InstanceResource = require('../lib/resource/InstanceResource'); 8 | var DataStore = require('../lib/ds/DataStore'); 9 | var Phone = require('../lib/resource/Phone'); 10 | var Account = require('../lib/resource/Account'); 11 | 12 | var phoneData = { 13 | href: 'https://api.stormpath.com/v1/phones/7lHGSpTvuxNnvnCkpOwUiR', 14 | createdAt: '2016-09-22T16:52:50.136Z', 15 | modifiedAt: '2016-09-22T16:52:50.136Z', 16 | number: '+12675555555', 17 | description: null, 18 | name: null, 19 | verificationStatus: 'UNVERIFIED', 20 | status: 'ENABLED', 21 | account: { 22 | href: 'https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spexaMple' 23 | } 24 | }; 25 | 26 | describe('Phone resource', function() { 27 | var sandbox; 28 | var dataStore; 29 | var phone; 30 | var getResourceStub; 31 | 32 | before(function() { 33 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 34 | sandbox = sinon.sandbox.create(); 35 | getResourceStub = sinon.stub(dataStore, 'getResource'); 36 | phone = new Phone(phoneData, dataStore); 37 | }); 38 | 39 | after(function() { 40 | sandbox.restore(); 41 | }); 42 | 43 | describe('constructor', function() { 44 | var superSpy; 45 | 46 | before(function() { 47 | superSpy = sandbox.spy(Phone, 'super_'); 48 | 49 | new Phone(phoneData, dataStore); 50 | }); 51 | 52 | it('should call super_ with the same arguments', function() { 53 | /*jshint -W030 */ 54 | superSpy.should.have.been.calledOnce; 55 | superSpy.should.have.been.calledWithExactly(phoneData, dataStore); 56 | /*jshint +W030 */ 57 | }); 58 | }); 59 | 60 | describe('instantiation and inheritance', function() { 61 | it('should inherit from InstanceResource', function() { 62 | assert.instanceOf(phone, InstanceResource); 63 | }); 64 | 65 | it('should be an instance of Phone', function() { 66 | assert.instanceOf(phone, Phone); 67 | }); 68 | }); 69 | 70 | describe('#getAccount(options, callback)', function() { 71 | var callback; 72 | 73 | before(function() { 74 | callback = sinon.spy(); 75 | phone.getAccount(callback); 76 | }); 77 | 78 | it('should call dataStore#getResource', function() { 79 | /*jshint -W030 */ 80 | getResourceStub.should.have.been.calledOnce; 81 | /*jshint +W030 */ 82 | }); 83 | 84 | it('should pass the correct href to dataStore#getResource', function() { 85 | getResourceStub.args[0][0].should.equal(phoneData.account.href); 86 | }); 87 | 88 | it('should pass no options to dataStore#getResource', function() { 89 | /*jshint -W030 */ 90 | getResourceStub.args[0][1].should.be.empty; 91 | /*jshint +W030 */ 92 | }); 93 | 94 | it('should pass the constructor for Account to dataStore#getResource', function() { 95 | getResourceStub.args[0][2].should.equal(Account); 96 | }); 97 | 98 | it('should pass the callback to dataStore#getResource', function() { 99 | getResourceStub.args[0][3].should.equal(callback); 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /test/sp.resource.resource_test.js: -------------------------------------------------------------------------------- 1 | var common = require('./common'); 2 | var _ = common._; 3 | var should = common.should; 4 | 5 | var Resource = require('../lib/resource/Resource'); 6 | var DataStore = require('../lib/ds/DataStore'); 7 | 8 | describe('Resources: ', function () { 9 | describe('Resource base class', function () { 10 | describe('constructor', function () { 11 | var apiKey; 12 | 13 | before(function () { 14 | apiKey = {id: 'id', secret: 'secret'}; 15 | }); 16 | 17 | describe('if called with 2 params', function () { 18 | var resource, ds; 19 | var obj; 20 | 21 | before(function () { 22 | obj = {data: 'boom!', data2: 'boom2!'}; 23 | ds = new DataStore({client: {apiKey:apiKey}}); 24 | resource = new Resource(obj, ds); 25 | }); 26 | 27 | it('should copy all fields from data', function () { 28 | _.each(obj, function (val, key) { 29 | resource[key].should.be.equal(val); 30 | }); 31 | }); 32 | it('should persist data store instance', function () { 33 | resource.dataStore.should.be.an.instanceof(DataStore); 34 | resource.dataStore.should.be.equal(ds); 35 | }); 36 | }); 37 | 38 | describe('if called only with data param', function () { 39 | var resource; 40 | var obj; 41 | 42 | before(function () { 43 | obj = {data: 'boom!', data2: 'boom2!'}; 44 | resource = new Resource(obj); 45 | }); 46 | 47 | it('should copy all fields from data param', function () { 48 | _.each(obj, function (val, key) { 49 | resource[key].should.be.equal(val); 50 | }); 51 | }); 52 | it('should leave dataStore empty', function(){ 53 | should.not.exist(resource.dataStore); 54 | }); 55 | }); 56 | 57 | describe('if called only with data store param', function () { 58 | var resource, ds; 59 | var hack; 60 | 61 | before(function () { 62 | hack = 'boom!'; 63 | ds = new DataStore({client: {apiKey:apiKey}}); 64 | ds.hack = hack; 65 | 66 | resource = new Resource(ds); 67 | }); 68 | 69 | it('should not copy any fields from data param', function(){ 70 | should.not.exist(resource.hack); 71 | }); 72 | it('should persist data store instance', function(){ 73 | resource.dataStore.should.be.an.instanceof(DataStore); 74 | resource.dataStore.should.be.equal(ds); 75 | }); 76 | }); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/sp.resource.schema_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var common = require('./common'); 4 | var assert = common.assert; 5 | var sinon = common.sinon; 6 | 7 | var DataStore = require('../lib/ds/DataStore'); 8 | var Field = require('../lib/resource/Field'); 9 | var Schema = require('../lib/resource/Schema'); 10 | 11 | describe('Schema Resource', function(){ 12 | 13 | describe('getFields()', function(){ 14 | 15 | var sandbox; 16 | var schema; 17 | var dataStore; 18 | 19 | var mockSchema = { 20 | href: 'https://api.stormpath.com/v1/schemas/7cvoYkLuGzpnAuVlKQdiDf/', 21 | fields: { 22 | href: 'https://api.stormpath.com/v1/schemas/7cvoYkLuGzpnAuVlKQdiDf/fields' 23 | } 24 | }; 25 | 26 | var mockFieldsResponse = { 27 | href: 'https://api.stormpath.com/v1/schemas/7dDfMLQmkARN4mQK9MPGIJ/fields', 28 | offset: 0, 29 | limit: 25, 30 | size: 2, 31 | items: [ 32 | { 33 | href: 'https://api.stormpath.com/v1/fields/7dDfMOkrekkLhbWBLcGWuN', 34 | createdAt: '2016-08-02T20:16:21.931Z', 35 | modifiedAt: '2016-08-02T20:16:21.931Z', 36 | name: 'givenName', 37 | required: true, 38 | schema: { 39 | href: 'https://api.stormpath.com/v1/schemas/7dDfMLQmkARN4mQK9MPGIJ' 40 | } 41 | }, 42 | ] 43 | }; 44 | 45 | 46 | 47 | before(function(){ 48 | dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); 49 | sandbox = sinon.sandbox.create(); 50 | schema = new Schema(mockSchema, dataStore); 51 | 52 | sandbox.stub(dataStore.requestExecutor, 'execute', function (req, cb) { 53 | cb(null, mockFieldsResponse); 54 | }); 55 | }); 56 | 57 | after(function(){ 58 | sandbox.restore(); 59 | }); 60 | 61 | it('should return Field instances', function(done){ 62 | schema.getFields(function(err, result){ 63 | assert.equal(result.items, mockFieldsResponse.items); 64 | assert.instanceOf(mockFieldsResponse.items[0], Field); 65 | done(); 66 | }); 67 | }); 68 | }); 69 | }); --------------------------------------------------------------------------------