{"action":"created","comment":{"url":"https://api.github.com/repos/oaeproject/Hilary/pulls/comments/32739423","id":32739423,"diff_hunk":"@@ -217,75 +238,78 @@ var _cacheTenants = function(callback) {\n  * @param  {String}     alias                   The unique alias for the tenant\n  * @param  {String}     displayName             A descriptive short name for the tenant\n  * @param  {String}     host                    The host on which this tenant will be proxying (e.g. oae.cam.ac.uk or oae.gatech.edu)\n+ * @param  {Object}     [opts]                  Optional arguments\n+ * @param  {String}     [opts.emailDomain]      The email domain for this tenant\n  * @param  {Function}   callback                Standard callback function\n  * @param  {Object}     callback.err            An error that occurred, if any\n  * @param  {Tenant}     callback.tenant         The created tenant\n  */\n-var createTenant = module.exports.createTenant = function(ctx, alias, displayName, host, callback) {\n-    callback = callback || function() {};\n-\n-    if (!ctx.user() || !ctx.user().isGlobalAdmin()) {\n-        return callback({'code': 401, 'msg': 'Only global administrators can create new tenants'});\n-    }\n+var createTenant = module.exports.createTenant = function(ctx, alias, displayName, host, opts, callback) {\n+    opts = opts || {};\n \n     var validator = new Validator();\n+    validator.check(null, {'code': 401, 'msg': 'Only global administrators can create new tenants'}).isGlobalAdministratorUser(ctx);\n     validator.check(alias, {'code': 400, 'msg': 'Missing alias'}).notEmpty();\n     validator.check(alias, {'code': 400, 'msg': 'The tenant alias should not contain a space'}).notContains(' ');\n     validator.check(alias, {'code': 400, 'msg': 'The tenant alias should not contain a colon'}).notContains(':');\n     validator.check(displayName, {'code': 400, 'msg': 'Missing tenant displayName'}).notEmpty();\n     validator.check(host, {'code': 400, 'msg': 'Missing tenant host'}).notEmpty();\n-    if (host) {\n-        validator.check(host.toLowerCase(), {'code': 400, 'msg': 'This hostname is reserved'}).not(serverConfig.shibbolethSPHost);\n-    }\n     if (validator.hasErrors()) {\n         return callback(validator.getFirstError());\n     }\n \n-    // Make sure the tenant alias and host name are all lower-case\n+    // Make sure the tenant alias, host name and email domain are all lower-case\n     alias = alias.toLowerCase();\n     host = host.toLowerCase();\n \n-    // Make sure that a tenant with the same alias doesn't exist yet\n-    if (!getTenant(alias)) {\n-        // Make sure that a tenant with the same hostname doesn't exist yet\n-        if (!getTenantByHost(host)) {\n-            // Create the tenant\n-            var tenant = new Tenant(alias, displayName, host);\n-            Cassandra.runQuery('UPDATE \"Tenant\" SET \"displayName\" = ?, \"host\" = ?, \"active\" = ? WHERE \"alias\" = ?',\n-                [tenant.displayName, host, tenant.active, tenant.alias], function(err) {\n-                if (err) {\n-                    return callback(err);\n-                }\n-\n-                // This event is not strictly necessary as it will be emitted by our PubSub publisher\n-                // as well. We emit it before we return to the caller so our unit tests can keep track\n-                // of config updates BEFORE the REST request completes. If we didn't there would be a\n-                // short period between the REST request returning and the config re-caching where the\n-                // config would be out-of-date. The length of this period is determined by how fast Redis\n-                // can broadcast the pubsub messages. When the system is under load and suffering from IO\n-                // starvation (such as during unit tests) this could lead to intermittent test failures.\n-                // The downside of emitting this event here is that it will lead to authentication strategies\n-                // and config elements being recached twice\n-                TenantsAPI.emit('created', tenant);\n-\n-                // Indicate that a caching operation is pending\n-                TenantsAPI.emit('preCache');\n-\n-                // Send a message to all the app servers in the cluster notifying them that the tenant should be started\n-                Pubsub.publish('oae-tenants', 'created ' + tenant.alias, function(err) {\n-                    if (err) {\n-                        return callback(err);\n-                    }\n-\n-                    return callback(null, tenant);\n-                });\n-            });\n-        } else {\n-            callback({'code': 400, 'msg': 'A tenant with the host ' + host + ' already exists'});\n+    // Ensure there are no conflicts\n+    validator.check(host, {'code': 400, 'msg': 'This hostname is reserved'}).not(serverConfig.shibbolethSPHost);\n+    validator.check(getTenant(alias), {'code': 400, 'msg': util.format('A tenant with the alias \"%s\" already exists', alias)}).isNull();\n+    validator.check(getTenantByHost(host), {'code': 400, 'msg': util.format('A tenant with the host \"%s\" already exists', host)}).isNull();\n+\n+    // Ensure only valid optional fields are set\n+    _.each(opts, function(val, key) {\n+        validator.check(key, {'code': 400, 'msg': util.format('Invalid field: %s', key)}).isIn(['emailDomain']);\n+        if (key === 'emailDomain' && _.isString(val)) {","path":"node_modules/oae-tenants/lib/api.js","position":141,"original_position":141,"commit_id":"0d57dc63212d480da3ebde6e249b6ccff3f75e4e","original_commit_id":"0d57dc63212d480da3ebde6e249b6ccff3f75e4e","user":{"login":"simong","id":92180,"avatar_url":"https://avatars.githubusercontent.com/u/92180?v=3","gravatar_id":"","url":"https://api.github.com/users/simong","html_url":"https://github.com/simong","followers_url":"https://api.github.com/users/simong/followers","following_url":"https://api.github.com/users/simong/following{/other_user}","gists_url":"https://api.github.com/users/simong/gists{/gist_id}","starred_url":"https://api.github.com/users/simong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simong/subscriptions","organizations_url":"https://api.github.com/users/simong/orgs","repos_url":"https://api.github.com/users/simong/repos","events_url":"https://api.github.com/users/simong/events{/privacy}","received_events_url":"https://api.github.com/users/simong/received_events","type":"User","site_admin":false},"body":"If `val` is not a string, this validator won't run which might cause issues","created_at":"2015-06-18T15:01:10Z","updated_at":"2015-06-18T15:01:10Z","html_url":"https://github.com/oaeproject/Hilary/pull/1202#discussion_r32739423","pull_request_url":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202","_links":{"self":{"href":"https://api.github.com/repos/oaeproject/Hilary/pulls/comments/32739423"},"html":{"href":"https://github.com/oaeproject/Hilary/pull/1202#discussion_r32739423"},"pull_request":{"href":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202"}}},"pull_request":{"url":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202","id":37652004,"html_url":"https://github.com/oaeproject/Hilary/pull/1202","diff_url":"https://github.com/oaeproject/Hilary/pull/1202.diff","patch_url":"https://github.com/oaeproject/Hilary/pull/1202.patch","issue_url":"https://api.github.com/repos/oaeproject/Hilary/issues/1202","number":1202,"state":"open","locked":false,"title":"(InvitingGuests/TenantDomains) Add ability specify an email domain expression for a tenant","user":{"login":"mrvisser","id":102265,"avatar_url":"https://avatars.githubusercontent.com/u/102265?v=3","gravatar_id":"","url":"https://api.github.com/users/mrvisser","html_url":"https://github.com/mrvisser","followers_url":"https://api.github.com/users/mrvisser/followers","following_url":"https://api.github.com/users/mrvisser/following{/other_user}","gists_url":"https://api.github.com/users/mrvisser/gists{/gist_id}","starred_url":"https://api.github.com/users/mrvisser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrvisser/subscriptions","organizations_url":"https://api.github.com/users/mrvisser/orgs","repos_url":"https://api.github.com/users/mrvisser/repos","events_url":"https://api.github.com/users/mrvisser/events{/privacy}","received_events_url":"https://api.github.com/users/mrvisser/received_events","type":"User","site_admin":false},"body":"Depends on oae-rest: oaeproject/oae-rest#20","created_at":"2015-06-15T00:54:02Z","updated_at":"2015-06-18T15:01:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ee10b532983f379335be7e682fbb40975267ae72","assignee":{"login":"simong","id":92180,"avatar_url":"https://avatars.githubusercontent.com/u/92180?v=3","gravatar_id":"","url":"https://api.github.com/users/simong","html_url":"https://github.com/simong","followers_url":"https://api.github.com/users/simong/followers","following_url":"https://api.github.com/users/simong/following{/other_user}","gists_url":"https://api.github.com/users/simong/gists{/gist_id}","starred_url":"https://api.github.com/users/simong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simong/subscriptions","organizations_url":"https://api.github.com/users/simong/orgs","repos_url":"https://api.github.com/users/simong/repos","events_url":"https://api.github.com/users/simong/events{/privacy}","received_events_url":"https://api.github.com/users/simong/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202/commits","review_comments_url":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202/comments","review_comment_url":"https://api.github.com/repos/oaeproject/Hilary/pulls/comments{/number}","comments_url":"https://api.github.com/repos/oaeproject/Hilary/issues/1202/comments","statuses_url":"https://api.github.com/repos/oaeproject/Hilary/statuses/0d57dc63212d480da3ebde6e249b6ccff3f75e4e","head":{"label":"mrvisser:tenant-domains","ref":"tenant-domains","sha":"0d57dc63212d480da3ebde6e249b6ccff3f75e4e","user":{"login":"mrvisser","id":102265,"avatar_url":"https://avatars.githubusercontent.com/u/102265?v=3","gravatar_id":"","url":"https://api.github.com/users/mrvisser","html_url":"https://github.com/mrvisser","followers_url":"https://api.github.com/users/mrvisser/followers","following_url":"https://api.github.com/users/mrvisser/following{/other_user}","gists_url":"https://api.github.com/users/mrvisser/gists{/gist_id}","starred_url":"https://api.github.com/users/mrvisser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrvisser/subscriptions","organizations_url":"https://api.github.com/users/mrvisser/orgs","repos_url":"https://api.github.com/users/mrvisser/repos","events_url":"https://api.github.com/users/mrvisser/events{/privacy}","received_events_url":"https://api.github.com/users/mrvisser/received_events","type":"User","site_admin":false},"repo":{"id":5658997,"name":"Hilary","full_name":"mrvisser/Hilary","owner":{"login":"mrvisser","id":102265,"avatar_url":"https://avatars.githubusercontent.com/u/102265?v=3","gravatar_id":"","url":"https://api.github.com/users/mrvisser","html_url":"https://github.com/mrvisser","followers_url":"https://api.github.com/users/mrvisser/followers","following_url":"https://api.github.com/users/mrvisser/following{/other_user}","gists_url":"https://api.github.com/users/mrvisser/gists{/gist_id}","starred_url":"https://api.github.com/users/mrvisser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrvisser/subscriptions","organizations_url":"https://api.github.com/users/mrvisser/orgs","repos_url":"https://api.github.com/users/mrvisser/repos","events_url":"https://api.github.com/users/mrvisser/events{/privacy}","received_events_url":"https://api.github.com/users/mrvisser/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mrvisser/Hilary","description":"","fork":true,"url":"https://api.github.com/repos/mrvisser/Hilary","forks_url":"https://api.github.com/repos/mrvisser/Hilary/forks","keys_url":"https://api.github.com/repos/mrvisser/Hilary/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrvisser/Hilary/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrvisser/Hilary/teams","hooks_url":"https://api.github.com/repos/mrvisser/Hilary/hooks","issue_events_url":"https://api.github.com/repos/mrvisser/Hilary/issues/events{/number}","events_url":"https://api.github.com/repos/mrvisser/Hilary/events","assignees_url":"https://api.github.com/repos/mrvisser/Hilary/assignees{/user}","branches_url":"https://api.github.com/repos/mrvisser/Hilary/branches{/branch}","tags_url":"https://api.github.com/repos/mrvisser/Hilary/tags","blobs_url":"https://api.github.com/repos/mrvisser/Hilary/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrvisser/Hilary/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrvisser/Hilary/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrvisser/Hilary/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrvisser/Hilary/statuses/{sha}","languages_url":"https://api.github.com/repos/mrvisser/Hilary/languages","stargazers_url":"https://api.github.com/repos/mrvisser/Hilary/stargazers","contributors_url":"https://api.github.com/repos/mrvisser/Hilary/contributors","subscribers_url":"https://api.github.com/repos/mrvisser/Hilary/subscribers","subscription_url":"https://api.github.com/repos/mrvisser/Hilary/subscription","commits_url":"https://api.github.com/repos/mrvisser/Hilary/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrvisser/Hilary/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrvisser/Hilary/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrvisser/Hilary/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrvisser/Hilary/contents/{+path}","compare_url":"https://api.github.com/repos/mrvisser/Hilary/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrvisser/Hilary/merges","archive_url":"https://api.github.com/repos/mrvisser/Hilary/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrvisser/Hilary/downloads","issues_url":"https://api.github.com/repos/mrvisser/Hilary/issues{/number}","pulls_url":"https://api.github.com/repos/mrvisser/Hilary/pulls{/number}","milestones_url":"https://api.github.com/repos/mrvisser/Hilary/milestones{/number}","notifications_url":"https://api.github.com/repos/mrvisser/Hilary/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrvisser/Hilary/labels{/name}","releases_url":"https://api.github.com/repos/mrvisser/Hilary/releases{/id}","created_at":"2012-09-03T13:08:54Z","updated_at":"2014-06-23T16:51:00Z","pushed_at":"2015-06-18T12:35:06Z","git_url":"git://github.com/mrvisser/Hilary.git","ssh_url":"git@github.com:mrvisser/Hilary.git","clone_url":"https://github.com/mrvisser/Hilary.git","svn_url":"https://github.com/mrvisser/Hilary","homepage":null,"size":27257,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"oaeproject:master","ref":"master","sha":"77fd91e1e33399c1234585c5fcd15dc3d96cf53c","user":{"login":"oaeproject","id":4659912,"avatar_url":"https://avatars.githubusercontent.com/u/4659912?v=3","gravatar_id":"","url":"https://api.github.com/users/oaeproject","html_url":"https://github.com/oaeproject","followers_url":"https://api.github.com/users/oaeproject/followers","following_url":"https://api.github.com/users/oaeproject/following{/other_user}","gists_url":"https://api.github.com/users/oaeproject/gists{/gist_id}","starred_url":"https://api.github.com/users/oaeproject/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oaeproject/subscriptions","organizations_url":"https://api.github.com/users/oaeproject/orgs","repos_url":"https://api.github.com/users/oaeproject/repos","events_url":"https://api.github.com/users/oaeproject/events{/privacy}","received_events_url":"https://api.github.com/users/oaeproject/received_events","type":"Organization","site_admin":false},"repo":{"id":5638565,"name":"Hilary","full_name":"oaeproject/Hilary","owner":{"login":"oaeproject","id":4659912,"avatar_url":"https://avatars.githubusercontent.com/u/4659912?v=3","gravatar_id":"","url":"https://api.github.com/users/oaeproject","html_url":"https://github.com/oaeproject","followers_url":"https://api.github.com/users/oaeproject/followers","following_url":"https://api.github.com/users/oaeproject/following{/other_user}","gists_url":"https://api.github.com/users/oaeproject/gists{/gist_id}","starred_url":"https://api.github.com/users/oaeproject/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oaeproject/subscriptions","organizations_url":"https://api.github.com/users/oaeproject/orgs","repos_url":"https://api.github.com/users/oaeproject/repos","events_url":"https://api.github.com/users/oaeproject/events{/privacy}","received_events_url":"https://api.github.com/users/oaeproject/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/oaeproject/Hilary","description":"Open Academic Environment (OAE) Back-End","fork":false,"url":"https://api.github.com/repos/oaeproject/Hilary","forks_url":"https://api.github.com/repos/oaeproject/Hilary/forks","keys_url":"https://api.github.com/repos/oaeproject/Hilary/keys{/key_id}","collaborators_url":"https://api.github.com/repos/oaeproject/Hilary/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/oaeproject/Hilary/teams","hooks_url":"https://api.github.com/repos/oaeproject/Hilary/hooks","issue_events_url":"https://api.github.com/repos/oaeproject/Hilary/issues/events{/number}","events_url":"https://api.github.com/repos/oaeproject/Hilary/events","assignees_url":"https://api.github.com/repos/oaeproject/Hilary/assignees{/user}","branches_url":"https://api.github.com/repos/oaeproject/Hilary/branches{/branch}","tags_url":"https://api.github.com/repos/oaeproject/Hilary/tags","blobs_url":"https://api.github.com/repos/oaeproject/Hilary/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/oaeproject/Hilary/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/oaeproject/Hilary/git/refs{/sha}","trees_url":"https://api.github.com/repos/oaeproject/Hilary/git/trees{/sha}","statuses_url":"https://api.github.com/repos/oaeproject/Hilary/statuses/{sha}","languages_url":"https://api.github.com/repos/oaeproject/Hilary/languages","stargazers_url":"https://api.github.com/repos/oaeproject/Hilary/stargazers","contributors_url":"https://api.github.com/repos/oaeproject/Hilary/contributors","subscribers_url":"https://api.github.com/repos/oaeproject/Hilary/subscribers","subscription_url":"https://api.github.com/repos/oaeproject/Hilary/subscription","commits_url":"https://api.github.com/repos/oaeproject/Hilary/commits{/sha}","git_commits_url":"https://api.github.com/repos/oaeproject/Hilary/git/commits{/sha}","comments_url":"https://api.github.com/repos/oaeproject/Hilary/comments{/number}","issue_comment_url":"https://api.github.com/repos/oaeproject/Hilary/issues/comments{/number}","contents_url":"https://api.github.com/repos/oaeproject/Hilary/contents/{+path}","compare_url":"https://api.github.com/repos/oaeproject/Hilary/compare/{base}...{head}","merges_url":"https://api.github.com/repos/oaeproject/Hilary/merges","archive_url":"https://api.github.com/repos/oaeproject/Hilary/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/oaeproject/Hilary/downloads","issues_url":"https://api.github.com/repos/oaeproject/Hilary/issues{/number}","pulls_url":"https://api.github.com/repos/oaeproject/Hilary/pulls{/number}","milestones_url":"https://api.github.com/repos/oaeproject/Hilary/milestones{/number}","notifications_url":"https://api.github.com/repos/oaeproject/Hilary/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/oaeproject/Hilary/labels{/name}","releases_url":"https://api.github.com/repos/oaeproject/Hilary/releases{/id}","created_at":"2012-09-01T10:05:13Z","updated_at":"2015-05-06T15:17:28Z","pushed_at":"2015-06-16T13:22:48Z","git_url":"git://github.com/oaeproject/Hilary.git","ssh_url":"git@github.com:oaeproject/Hilary.git","clone_url":"https://github.com/oaeproject/Hilary.git","svn_url":"https://github.com/oaeproject/Hilary","homepage":"http://www.oaeproject.org","size":30346,"stargazers_count":32,"watchers_count":32,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":65,"mirror_url":null,"open_issues_count":29,"forks":65,"open_issues":29,"watchers":32,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202"},"html":{"href":"https://github.com/oaeproject/Hilary/pull/1202"},"issue":{"href":"https://api.github.com/repos/oaeproject/Hilary/issues/1202"},"comments":{"href":"https://api.github.com/repos/oaeproject/Hilary/issues/1202/comments"},"review_comments":{"href":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202/comments"},"review_comment":{"href":"https://api.github.com/repos/oaeproject/Hilary/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/oaeproject/Hilary/pulls/1202/commits"},"statuses":{"href":"https://api.github.com/repos/oaeproject/Hilary/statuses/0d57dc63212d480da3ebde6e249b6ccff3f75e4e"}}}}