parent
63f076bdf8
commit
88b458b585
@ -1,35 +0,0 @@ |
||||
orig_updateOrCreateUserFromExternalService = Accounts.updateOrCreateUserFromExternalService |
||||
Accounts.updateOrCreateUserFromExternalService = (serviceName, serviceData, options) -> |
||||
|
||||
if serviceName not in ['facebook', 'github', 'gitlab', 'google', 'meteor-developer', 'linkedin', 'twitter', 'sandstorm'] and serviceData._OAuthCustom isnt true |
||||
return |
||||
|
||||
if serviceName is 'meteor-developer' |
||||
if _.isArray serviceData?.emails |
||||
serviceData.emails.sort (a, b) -> |
||||
return a.primary isnt true |
||||
|
||||
for email in serviceData.emails |
||||
if email.verified is true |
||||
serviceData.email = email.address |
||||
break |
||||
|
||||
if serviceName is 'linkedin' |
||||
serviceData.email = serviceData.emailAddress |
||||
|
||||
if serviceData.email |
||||
|
||||
# Find user with given email |
||||
user = RocketChat.models.Users.findOneByEmailAddress serviceData.email |
||||
if user? |
||||
# If email is not verified, reset password and require password change |
||||
if not _.findWhere user.emails, { address: serviceData.email, verified: true } |
||||
RocketChat.models.Users.resetPasswordAndSetRequirePasswordChange(user._id, true, 'This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password') |
||||
|
||||
# Merge accounts |
||||
RocketChat.models.Users.setServiceId user._id, serviceName, serviceData.id |
||||
|
||||
# Validate email |
||||
RocketChat.models.Users.setEmailVerified user._id, serviceData.email |
||||
|
||||
return orig_updateOrCreateUserFromExternalService.apply(this, arguments) |
||||
@ -0,0 +1,47 @@ |
||||
const orig_updateOrCreateUserFromExternalService = Accounts.updateOrCreateUserFromExternalService; |
||||
|
||||
Accounts.updateOrCreateUserFromExternalService = function(serviceName, serviceData = {} /*, options*/) { |
||||
const services = [ |
||||
'facebook', |
||||
'github', |
||||
'gitlab', |
||||
'google', |
||||
'meteor-developer', |
||||
'linkedin', |
||||
'twitter', |
||||
'sandstorm' |
||||
]; |
||||
|
||||
if (services.includes(serviceName) === false && serviceData._OAuthCustom !== true) { |
||||
return; |
||||
} |
||||
|
||||
if (serviceName === 'meteor-developer') { |
||||
if (Array.isArray(serviceData.emails)) { |
||||
serviceData.email = serviceData.emails.sort(a => a.primary !== true).filter(item => item.verified === true)[0]; |
||||
} |
||||
} |
||||
|
||||
if (serviceName === 'linkedin') { |
||||
serviceData.email = serviceData.emailAddress; |
||||
} |
||||
|
||||
if (serviceData.email) { |
||||
const user = RocketChat.models.Users.findOneByEmailAddress(serviceData.email); |
||||
if (user != null) { |
||||
const findQuery = { |
||||
address: serviceData.email, |
||||
verified: true |
||||
}; |
||||
|
||||
if (!_.findWhere(user.emails, findQuery)) { |
||||
RocketChat.models.Users.resetPasswordAndSetRequirePasswordChange(user._id, true, 'This_email_has_already_been_used_and_has_not_been_verified__Please_change_your_password'); |
||||
} |
||||
|
||||
RocketChat.models.Users.setServiceId(user._id, serviceName, serviceData.id); |
||||
RocketChat.models.Users.setEmailVerified(user._id, serviceData.email); |
||||
} |
||||
} |
||||
|
||||
return orig_updateOrCreateUserFromExternalService.apply(this, arguments); |
||||
}; |
||||
@ -1,171 +0,0 @@ |
||||
# Deny Account.createUser in client and set Meteor.loginTokenExpires |
||||
accountsConfig = { forbidClientAccountCreation: true, loginExpirationInDays: RocketChat.settings.get 'Accounts_LoginExpiration' } |
||||
Accounts.config accountsConfig |
||||
|
||||
Accounts.emailTemplates.siteName = RocketChat.settings.get 'Site_Name'; |
||||
Accounts.emailTemplates.from = "#{RocketChat.settings.get 'Site_Name'} <#{RocketChat.settings.get 'From_Email'}>"; |
||||
|
||||
verifyEmailHtml = Accounts.emailTemplates.verifyEmail.text |
||||
Accounts.emailTemplates.verifyEmail.html = (user, url) -> |
||||
url = url.replace Meteor.absoluteUrl(), Meteor.absoluteUrl() + 'login/' |
||||
verifyEmailHtml user, url |
||||
|
||||
resetPasswordHtml = Accounts.emailTemplates.resetPassword.text |
||||
Accounts.emailTemplates.resetPassword.html = (user, url) -> |
||||
url = url.replace /\/#\//, '/' |
||||
resetPasswordHtml user, url |
||||
|
||||
Accounts.emailTemplates.enrollAccount.subject = (user) -> |
||||
if RocketChat.settings.get 'Accounts_Enrollment_Customized' |
||||
subject = RocketChat.settings.get 'Accounts_Enrollment_Email_Subject' |
||||
else |
||||
subject = TAPi18n.__('Accounts_Enrollment_Email_Subject_Default', { lng: user?.language || RocketChat.settings.get('language') || 'en' }) |
||||
|
||||
return RocketChat.placeholders.replace(subject); |
||||
|
||||
Accounts.emailTemplates.enrollAccount.html = (user, url) -> |
||||
|
||||
if RocketChat.settings.get 'Accounts_Enrollment_Customized' |
||||
html = RocketChat.settings.get 'Accounts_Enrollment_Email' |
||||
else |
||||
html = TAPi18n.__('Accounts_Enrollment_Email_Default', { lng: user?.language || RocketChat.settings.get('language') || 'en' }) |
||||
|
||||
header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || "") |
||||
footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || "") |
||||
html = RocketChat.placeholders.replace(html, { |
||||
name: user.name, |
||||
email: user.emails?[0]?.address |
||||
}); |
||||
|
||||
return header + html + footer; |
||||
|
||||
Accounts.onCreateUser (options, user) -> |
||||
# console.log 'onCreateUser ->',JSON.stringify arguments, null, ' ' |
||||
# console.log 'options ->',JSON.stringify options, null, ' ' |
||||
# console.log 'user ->',JSON.stringify user, null, ' ' |
||||
|
||||
RocketChat.callbacks.run 'beforeCreateUser', options, user |
||||
|
||||
user.status = 'offline' |
||||
user.active = not RocketChat.settings.get 'Accounts_ManuallyApproveNewUsers' |
||||
|
||||
if not user?.name? or user.name is '' |
||||
if options.profile?.name? |
||||
user.name = options.profile?.name |
||||
|
||||
if user.services? |
||||
for serviceName, service of user.services |
||||
if not user?.name? or user.name is '' |
||||
if service.name? |
||||
user.name = service.name |
||||
else if service.username? |
||||
user.name = service.username |
||||
|
||||
if not user.emails? and service.email? |
||||
user.emails = [ |
||||
address: service.email |
||||
verified: true |
||||
] |
||||
|
||||
return user |
||||
|
||||
# Wrap insertUserDoc to allow executing code after Accounts.insertUserDoc is run |
||||
Accounts.insertUserDoc = _.wrap Accounts.insertUserDoc, (insertUserDoc, options, user) -> |
||||
roles = [] |
||||
if Match.test(user.globalRoles, [String]) and user.globalRoles.length > 0 |
||||
roles = roles.concat user.globalRoles |
||||
|
||||
delete user.globalRoles |
||||
|
||||
user.type ?= 'user' |
||||
|
||||
_id = insertUserDoc.call(Accounts, options, user) |
||||
|
||||
# Get user from db again to get all fields added by middlewares like |
||||
# validateNewUser and others. |
||||
user = Meteor.users.findOne({_id: _id}); |
||||
|
||||
# Add user to default channels |
||||
if user.username? and options.joinDefaultChannels isnt false and user.joinDefaultChannels isnt false |
||||
Meteor.runAsUser _id, -> |
||||
Meteor.call('joinDefaultChannels', options.joinDefaultChannelsSilenced) |
||||
|
||||
if roles.length is 0 |
||||
# when inserting first user give them admin privileges otherwise make a regular user |
||||
hasAdmin = RocketChat.models.Users.findOne({ roles: 'admin' }, {fields: {_id: 1}}) |
||||
if hasAdmin? |
||||
roles.push 'user' |
||||
else |
||||
roles.push 'admin' |
||||
|
||||
RocketChat.authz.addUserRoles(_id, roles) |
||||
|
||||
Meteor.defer -> |
||||
RocketChat.callbacks.run 'afterCreateUser', options, user |
||||
|
||||
return _id |
||||
|
||||
Accounts.validateLoginAttempt (login) -> |
||||
login = RocketChat.callbacks.run 'beforeValidateLogin', login |
||||
|
||||
if login.allowed isnt true |
||||
return login.allowed |
||||
|
||||
# bypass for livechat users |
||||
if login.user.type is 'visitor' |
||||
return true |
||||
|
||||
if !!login.user?.active isnt true |
||||
throw new Meteor.Error 'error-user-is-not-activated', 'User is not activated', { function: 'Accounts.validateLoginAttempt' } |
||||
return false |
||||
|
||||
# If user is admin, no need to check if email is verified |
||||
if 'admin' not in login.user?.roles and login.type is 'password' and RocketChat.settings.get('Accounts_EmailVerification') is true |
||||
validEmail = login.user.emails.filter (email) -> |
||||
return email.verified is true |
||||
|
||||
if validEmail.length is 0 |
||||
throw new Meteor.Error 'error-invalid-email', 'Invalid email __email__' |
||||
return false |
||||
|
||||
RocketChat.models.Users.updateLastLoginById login.user._id |
||||
|
||||
Meteor.defer -> |
||||
RocketChat.callbacks.run 'afterValidateLogin', login |
||||
|
||||
return true |
||||
|
||||
Accounts.validateNewUser (user) -> |
||||
# bypass for livechat users |
||||
if user.type is 'visitor' |
||||
return true |
||||
|
||||
if RocketChat.settings.get('Accounts_Registration_AuthenticationServices_Enabled') is false and RocketChat.settings.get('LDAP_Enable') is false and not user.services?.password? |
||||
throw new Meteor.Error 'registration-disabled-authentication-services', 'User registration is disabled for authentication services' |
||||
return true |
||||
|
||||
|
||||
Accounts.validateNewUser (user) -> |
||||
# bypass for livechat users |
||||
if user.type is 'visitor' |
||||
return true |
||||
|
||||
domainWhiteList = RocketChat.settings.get('Accounts_AllowedDomainsList') |
||||
|
||||
if _.isEmpty s.trim(domainWhiteList) |
||||
return true |
||||
|
||||
domainWhiteList = _.map(domainWhiteList.split(','), (domain) -> domain.trim()) |
||||
|
||||
if user.emails?.length > 0 |
||||
ret = false |
||||
email = user.emails[0].address |
||||
for domain in domainWhiteList |
||||
if email.match('@' + RegExp.escape(domain) + '$') |
||||
ret = true |
||||
break |
||||
|
||||
if not ret |
||||
throw new Meteor.Error 'error-invalid-domain' |
||||
|
||||
return true |
||||
@ -0,0 +1,204 @@ |
||||
const accountsConfig = { |
||||
forbidClientAccountCreation: true, |
||||
loginExpirationInDays: RocketChat.settings.get('Accounts_LoginExpiration') |
||||
}; |
||||
|
||||
Accounts.config(accountsConfig); |
||||
|
||||
Accounts.emailTemplates.siteName = RocketChat.settings.get('Site_Name'); |
||||
|
||||
Accounts.emailTemplates.from = `${RocketChat.settings.get('Site_Name')} <${RocketChat.settings.get('From_Email')}>`; |
||||
|
||||
const verifyEmailHtml = Accounts.emailTemplates.verifyEmail.text; |
||||
|
||||
Accounts.emailTemplates.verifyEmail.html = function(user, url) { |
||||
url = url.replace(Meteor.absoluteUrl(), `${Meteor.absoluteUrl()}login/`); |
||||
return verifyEmailHtml(user, url); |
||||
}; |
||||
|
||||
const resetPasswordHtml = Accounts.emailTemplates.resetPassword.text; |
||||
|
||||
Accounts.emailTemplates.resetPassword.html = function(user, url) { |
||||
url = url.replace(/\/#\//, '/'); |
||||
return resetPasswordHtml(user, url); |
||||
}; |
||||
|
||||
Accounts.emailTemplates.enrollAccount.subject = function(user = {}) { |
||||
let subject; |
||||
if (RocketChat.settings.get('Accounts_Enrollment_Customized')) { |
||||
subject = RocketChat.settings.get('Accounts_Enrollment_Email_Subject'); |
||||
} else { |
||||
subject = TAPi18n.__('Accounts_Enrollment_Email_Subject_Default', { |
||||
lng: user.language || RocketChat.settings.get('language') || 'en' |
||||
}); |
||||
} |
||||
return RocketChat.placeholders.replace(subject); |
||||
}; |
||||
|
||||
Accounts.emailTemplates.enrollAccount.html = function(user = {}/*, url*/) { |
||||
let html; |
||||
if (RocketChat.settings.get('Accounts_Enrollment_Customized')) { |
||||
html = RocketChat.settings.get('Accounts_Enrollment_Email'); |
||||
} else { |
||||
html = TAPi18n.__('Accounts_Enrollment_Email_Default', { |
||||
lng: user.language || RocketChat.settings.get('language') || 'en' |
||||
}); |
||||
} |
||||
|
||||
const header = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Header') || ''); |
||||
const footer = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer') || ''); |
||||
|
||||
html = RocketChat.placeholders.replace(html, { |
||||
name: user.name, |
||||
email: user.emails && user.emails[0] && user.emails[0].address |
||||
}); |
||||
|
||||
return header + html + footer; |
||||
}; |
||||
|
||||
Accounts.onCreateUser(function(options, user = {}) { |
||||
RocketChat.callbacks.run('beforeCreateUser', options, user); |
||||
|
||||
user.status = 'offline'; |
||||
user.active = !RocketChat.settings.get('Accounts_ManuallyApproveNewUsers'); |
||||
|
||||
if (!user.name) { |
||||
if (options.profile && options.profile.name) { |
||||
user.name = options.profile.name; |
||||
} |
||||
} |
||||
|
||||
if (user.services) { |
||||
for (const service of Object.values(user.services)) { |
||||
if (!user.name) { |
||||
user.name = service.name || service.username; |
||||
} |
||||
|
||||
if (!user.emails && service.email) { |
||||
user.emails = [{ |
||||
address: service.email, |
||||
verified: true |
||||
}]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return user; |
||||
}); |
||||
|
||||
Accounts.insertUserDoc = _.wrap(Accounts.insertUserDoc, function(insertUserDoc, options, user) { |
||||
let roles = []; |
||||
|
||||
if (Match.test(user.globalRoles, [String]) && user.globalRoles.length > 0) { |
||||
roles = roles.concat(user.globalRoles); |
||||
} |
||||
|
||||
delete user.globalRoles; |
||||
|
||||
if (!user.type) { |
||||
user.type = 'user'; |
||||
} |
||||
|
||||
const _id = insertUserDoc.call(Accounts, options, user); |
||||
|
||||
user = Meteor.users.findOne({ |
||||
_id: _id |
||||
}); |
||||
|
||||
if (user.username && options.joinDefaultChannels !== false && user.joinDefaultChannels !== false) { |
||||
Meteor.runAsUser(_id, function() { |
||||
return Meteor.call('joinDefaultChannels', options.joinDefaultChannelsSilenced); |
||||
}); |
||||
} |
||||
|
||||
if (roles.length === 0) { |
||||
const hasAdmin = RocketChat.models.Users.findOne({ |
||||
roles: 'admin' |
||||
}, { |
||||
fields: { |
||||
_id: 1 |
||||
} |
||||
}); |
||||
|
||||
if (hasAdmin) { |
||||
roles.push('user'); |
||||
} else { |
||||
roles.push('admin'); |
||||
} |
||||
} |
||||
|
||||
RocketChat.authz.addUserRoles(_id, roles); |
||||
Meteor.defer(function() { |
||||
return RocketChat.callbacks.run('afterCreateUser', options, user); |
||||
}); |
||||
|
||||
return _id; |
||||
}); |
||||
|
||||
Accounts.validateLoginAttempt(function(login) { |
||||
login = RocketChat.callbacks.run('beforeValidateLogin', login); |
||||
|
||||
if (login.allowed !== true) { |
||||
return login.allowed; |
||||
} |
||||
|
||||
if (login.user.type === 'visitor') { |
||||
return true; |
||||
} |
||||
|
||||
if (!!login.user.active !== true) { |
||||
throw new Meteor.Error('error-user-is-not-activated', 'User is not activated', { |
||||
'function': 'Accounts.validateLoginAttempt' |
||||
}); |
||||
} |
||||
|
||||
if (login.user.roles.includes('admin') === false && login.type === 'password' && RocketChat.settings.get('Accounts_EmailVerification') === true) { |
||||
const validEmail = login.user.emails.filter(email => email.verified === true); |
||||
if (validEmail.length === 0) { |
||||
throw new Meteor.Error('error-invalid-email', 'Invalid email __email__'); |
||||
} |
||||
} |
||||
|
||||
RocketChat.models.Users.updateLastLoginById(login.user._id); |
||||
Meteor.defer(function() { |
||||
return RocketChat.callbacks.run('afterValidateLogin', login); |
||||
}); |
||||
|
||||
return true; |
||||
}); |
||||
|
||||
Accounts.validateNewUser(function(user) { |
||||
if (user.type === 'visitor') { |
||||
return true; |
||||
} |
||||
|
||||
if (RocketChat.settings.get('Accounts_Registration_AuthenticationServices_Enabled') === false && RocketChat.settings.get('LDAP_Enable') === false && !(user.services && user.services.password)) { |
||||
throw new Meteor.Error('registration-disabled-authentication-services', 'User registration is disabled for authentication services'); |
||||
} |
||||
|
||||
return true; |
||||
}); |
||||
|
||||
Accounts.validateNewUser(function(user) { |
||||
if (user.type === 'visitor') { |
||||
return true; |
||||
} |
||||
|
||||
let domainWhiteList = RocketChat.settings.get('Accounts_AllowedDomainsList'); |
||||
if (_.isEmpty(s.trim(domainWhiteList))) { |
||||
return true; |
||||
} |
||||
|
||||
domainWhiteList = domainWhiteList.split(',').map(domain => domain.trim()); |
||||
|
||||
if (user.emails && user.emails.length > 0) { |
||||
const email = user.emails[0].address; |
||||
const inWhiteList = domainWhiteList.some(domain => email.match('@' + RegExp.escape(domain) + '$')); |
||||
|
||||
if (inWhiteList === false) { |
||||
throw new Meteor.Error('error-invalid-domain'); |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
}); |
||||
@ -1,188 +0,0 @@ |
||||
Meteor.methods |
||||
log: -> |
||||
console.log.apply console, arguments |
||||
|
||||
push_test: -> |
||||
user = Meteor.user() |
||||
if not user? |
||||
throw new Meteor.Error 'error-not-allowed', 'Not allowed', { method: 'push_test' } |
||||
|
||||
if not RocketChat.authz.hasRole(user._id, 'admin') |
||||
throw new Meteor.Error 'error-not-allowed', 'Not allowed', { method: 'push_test' } |
||||
|
||||
if Push.enabled isnt true |
||||
throw new Meteor.Error 'error-push-disabled', 'Push is disabled', { method: 'push_test' } |
||||
|
||||
query = |
||||
$and: [ |
||||
userId: user._id |
||||
{ |
||||
$or: [ |
||||
{ 'token.apn': { $exists: true } } |
||||
{ 'token.gcm': { $exists: true } } |
||||
] |
||||
} |
||||
] |
||||
|
||||
tokens = Push.appCollection.find(query).count() |
||||
|
||||
if tokens is 0 |
||||
throw new Meteor.Error 'error-no-tokens-for-this-user', "There are no tokens for this user", { method: 'push_test' } |
||||
|
||||
Push.send |
||||
from: 'push' |
||||
title: "@#{user.username}" |
||||
text: TAPi18n.__ "This_is_a_push_test_messsage" |
||||
apn: |
||||
text: "@#{user.username}:\n" + TAPi18n.__ "This_is_a_push_test_messsage" |
||||
sound: 'default' |
||||
query: |
||||
userId: user._id |
||||
|
||||
return {} = |
||||
message: "Your_push_was_sent_to_s_devices" |
||||
params: [tokens] |
||||
|
||||
|
||||
configurePush = -> |
||||
if RocketChat.settings.get 'Push_debug' |
||||
Push.debug = true |
||||
console.log 'Push: configuring...' |
||||
|
||||
if RocketChat.settings.get('Push_enable') is true |
||||
Push.allow |
||||
send: (userId, notification) -> |
||||
return RocketChat.authz.hasRole(userId, 'admin') |
||||
|
||||
apn = undefined |
||||
gcm = undefined |
||||
|
||||
if RocketChat.settings.get('Push_enable_gateway') is false |
||||
gcm = |
||||
apiKey: RocketChat.settings.get 'Push_gcm_api_key' |
||||
projectNumber: RocketChat.settings.get 'Push_gcm_project_number' |
||||
|
||||
apn = |
||||
passphrase: RocketChat.settings.get 'Push_apn_passphrase' |
||||
keyData: RocketChat.settings.get 'Push_apn_key' |
||||
certData: RocketChat.settings.get 'Push_apn_cert' |
||||
|
||||
if RocketChat.settings.get('Push_production') isnt true |
||||
apn = |
||||
passphrase: RocketChat.settings.get 'Push_apn_dev_passphrase' |
||||
keyData: RocketChat.settings.get 'Push_apn_dev_key' |
||||
certData: RocketChat.settings.get 'Push_apn_dev_cert' |
||||
gateway: 'gateway.sandbox.push.apple.com' |
||||
|
||||
if not apn.keyData? or apn.keyData.trim() is '' or not apn.keyData? or apn.keyData.trim() is '' |
||||
apn = undefined |
||||
|
||||
if not gcm.apiKey? or gcm.apiKey.trim() is '' or not gcm.projectNumber? or gcm.projectNumber.trim() is '' |
||||
gcm = undefined |
||||
|
||||
Push.Configure |
||||
apn: apn |
||||
gcm: gcm |
||||
production: RocketChat.settings.get 'Push_production' |
||||
sendInterval: 1000 |
||||
sendBatchSize: 10 |
||||
|
||||
if RocketChat.settings.get('Push_enable_gateway') is true |
||||
Push.serverSend = (options) -> |
||||
options = options or { badge: 0 } |
||||
query = undefined |
||||
|
||||
if options.from isnt ''+options.from |
||||
throw new Error('Push.send: option "from" not a string') |
||||
|
||||
if options.title isnt ''+options.title |
||||
throw new Error('Push.send: option "title" not a string') |
||||
|
||||
if options.text isnt ''+options.text |
||||
throw new Error('Push.send: option "text" not a string') |
||||
|
||||
if RocketChat.settings.get 'Push_debug' |
||||
console.log('Push: send message "' + options.title + '" via query', options.query) |
||||
|
||||
query = |
||||
$and: [ |
||||
options.query |
||||
{ |
||||
$or: [ |
||||
{ 'token.apn': { $exists: true } } |
||||
{ 'token.gcm': { $exists: true } } |
||||
] |
||||
} |
||||
] |
||||
|
||||
Push.appCollection.find(query).forEach (app) -> |
||||
if RocketChat.settings.get 'Push_debug' |
||||
console.log('Push: send to token', app.token) |
||||
|
||||
if app.token.apn? |
||||
service = 'apn' |
||||
token = app.token.apn |
||||
else if app.token.gcm? |
||||
service = 'gcm' |
||||
token = app.token.gcm |
||||
|
||||
sendPush service, token, options |
||||
|
||||
Push.enabled = true |
||||
|
||||
sendPush = (service, token, options, tries=0) -> |
||||
data = |
||||
data: |
||||
token: token |
||||
options: options |
||||
|
||||
HTTP.post RocketChat.settings.get('Push_gateway') + "/push/#{service}/send", data, (error, response) -> |
||||
if response?.statusCode is 406 |
||||
console.log('removing push token', token) |
||||
Push.appCollection.remove({ |
||||
$or: [ |
||||
{ 'token.apn': token } |
||||
{ 'token.gcm': token } |
||||
] |
||||
}) |
||||
return |
||||
|
||||
if not error? |
||||
return |
||||
|
||||
SystemLogger.error 'Error sending push to gateway ('+tries+' try) ->', error |
||||
if tries <= 6 |
||||
milli = Math.pow(10, tries+2) |
||||
|
||||
SystemLogger.log 'Trying sending push to gateway again in', milli, 'milliseconds' |
||||
|
||||
# Try again in 0.1s, 1s, 10s, 1m40s, 16m40s, 2h46m40s and 27h46m40s |
||||
Meteor.setTimeout -> |
||||
sendPush service, token, options, tries+1 |
||||
, milli |
||||
|
||||
Meteor.startup -> |
||||
configurePush() |
||||
|
||||
## Prepared to reconfigure the push plugin |
||||
# |
||||
# keys = [ |
||||
# 'Push_enable' |
||||
# 'Push_enable_gateway' |
||||
# 'Push_gcm_api_key' |
||||
# 'Push_gcm_project_number' |
||||
# 'Push_apn_passphrase' |
||||
# 'Push_apn_key' |
||||
# 'Push_apn_cert' |
||||
# 'Push_production' |
||||
# 'Push_apn_dev_passphrase' |
||||
# 'Push_apn_dev_key' |
||||
# 'Push_apn_dev_cert' |
||||
# 'Push_gateway' |
||||
# ] |
||||
|
||||
# configurePushDebounce = _.debounce Meteor.bindEnvironment(configurePush), 1000 |
||||
|
||||
# RocketChat.settings.onload keys, -> |
||||
# configurePushDebounce() |
||||
|
||||
@ -0,0 +1,215 @@ |
||||
/* global Push, SystemLogger */ |
||||
|
||||
Meteor.methods({ |
||||
log() { |
||||
return console.log(...arguments); |
||||
}, |
||||
|
||||
push_test() { |
||||
const user = Meteor.user(); |
||||
|
||||
if (!user) { |
||||
throw new Meteor.Error('error-not-allowed', 'Not allowed', { |
||||
method: 'push_test' |
||||
}); |
||||
} |
||||
|
||||
if (!RocketChat.authz.hasRole(user._id, 'admin')) { |
||||
throw new Meteor.Error('error-not-allowed', 'Not allowed', { |
||||
method: 'push_test' |
||||
}); |
||||
} |
||||
|
||||
if (Push.enabled !== true) { |
||||
throw new Meteor.Error('error-push-disabled', 'Push is disabled', { |
||||
method: 'push_test' |
||||
}); |
||||
} |
||||
|
||||
const query = { |
||||
$and: [{ |
||||
userId: user._id |
||||
}, { |
||||
$or: [{ |
||||
'token.apn': { |
||||
$exists: true |
||||
} |
||||
}, { |
||||
'token.gcm': { |
||||
$exists: true |
||||
} |
||||
}] |
||||
}] |
||||
}; |
||||
|
||||
const tokens = Push.appCollection.find(query).count(); |
||||
|
||||
if (tokens === 0) { |
||||
throw new Meteor.Error('error-no-tokens-for-this-user', 'There are no tokens for this user', { |
||||
method: 'push_test' |
||||
}); |
||||
} |
||||
|
||||
Push.send({ |
||||
from: 'push', |
||||
title: `@${user.username}`, |
||||
text: TAPi18n.__('This_is_a_push_test_messsage'), |
||||
apn: { |
||||
text: `@${user.username}:\n${TAPi18n.__('This_is_a_push_test_messsage')}` |
||||
}, |
||||
sound: 'default', |
||||
query: { |
||||
userId: user._id |
||||
} |
||||
}); |
||||
|
||||
return { |
||||
message: 'Your_push_was_sent_to_s_devices', |
||||
params: [tokens] |
||||
}; |
||||
} |
||||
}); |
||||
|
||||
function sendPush(service, token, options, tries = 0) { |
||||
const data = { |
||||
data: { |
||||
token: token, |
||||
options: options |
||||
} |
||||
}; |
||||
|
||||
return HTTP.post(RocketChat.settings.get('Push_gateway') + `/push/${service}/send`, data, function(error, response) { |
||||
if (response && response.statusCode === 406) { |
||||
console.log('removing push token', token); |
||||
Push.appCollection.remove({ |
||||
$or: [{ |
||||
'token.apn': token |
||||
}, { |
||||
'token.gcm': token |
||||
}] |
||||
}); |
||||
return; |
||||
} |
||||
|
||||
if (!error) { |
||||
return; |
||||
} |
||||
|
||||
SystemLogger.error(`Error sending push to gateway (${tries} try) ->`, error); |
||||
|
||||
if (tries <= 6) { |
||||
const milli = Math.pow(10, tries + 2); |
||||
|
||||
SystemLogger.log('Trying sending push to gateway again in', milli, 'milliseconds'); |
||||
|
||||
return Meteor.setTimeout(function() { |
||||
return sendPush(service, token, options, tries + 1); |
||||
}, milli); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
function configurePush() { |
||||
if (RocketChat.settings.get('Push_debug')) { |
||||
Push.debug = true; |
||||
console.log('Push: configuring...'); |
||||
} |
||||
|
||||
if (RocketChat.settings.get('Push_enable') === true) { |
||||
Push.allow({ |
||||
send: function(userId/*, notification*/) { |
||||
return RocketChat.authz.hasRole(userId, 'admin'); |
||||
} |
||||
}); |
||||
|
||||
let apn, gcm; |
||||
|
||||
if (RocketChat.settings.get('Push_enable_gateway') === false) { |
||||
gcm = { |
||||
apiKey: RocketChat.settings.get('Push_gcm_api_key'), |
||||
projectNumber: RocketChat.settings.get('Push_gcm_project_number') |
||||
}; |
||||
|
||||
apn = { |
||||
passphrase: RocketChat.settings.get('Push_apn_passphrase'), |
||||
keyData: RocketChat.settings.get('Push_apn_key'), |
||||
certData: RocketChat.settings.get('Push_apn_cert') |
||||
}; |
||||
|
||||
if (RocketChat.settings.get('Push_production') !== true) { |
||||
apn = { |
||||
passphrase: RocketChat.settings.get('Push_apn_dev_passphrase'), |
||||
keyData: RocketChat.settings.get('Push_apn_dev_key'), |
||||
certData: RocketChat.settings.get('Push_apn_dev_cert'), |
||||
gateway: 'gateway.sandbox.push.apple.com' |
||||
}; |
||||
} |
||||
|
||||
if (!apn.keyData || apn.keyData.trim() === '' || !apn.certData || apn.certData.trim() === '') { |
||||
apn = undefined; |
||||
} |
||||
|
||||
if (!gcm.apiKey || gcm.apiKey.trim() === '' || !gcm.projectNumber || gcm.projectNumber.trim() === '') { |
||||
gcm = undefined; |
||||
} |
||||
} |
||||
|
||||
Push.Configure({ |
||||
apn: apn, |
||||
gcm: gcm, |
||||
production: RocketChat.settings.get('Push_production'), |
||||
sendInterval: 1000, |
||||
sendBatchSize: 10 |
||||
}); |
||||
|
||||
if (RocketChat.settings.get('Push_enable_gateway') === true) { |
||||
Push.serverSend = function(options = {badge: 0}) { |
||||
if (options.from !== String(options.from)) { |
||||
throw new Error('Push.send: option "from" not a string'); |
||||
} |
||||
if (options.title !== String(options.title)) { |
||||
throw new Error('Push.send: option "title" not a string'); |
||||
} |
||||
if (options.text !== String(options.text)) { |
||||
throw new Error('Push.send: option "text" not a string'); |
||||
} |
||||
if (RocketChat.settings.get('Push_debug')) { |
||||
console.log(`Push: send message "${options.title}" via query`, options.query); |
||||
} |
||||
|
||||
const query = { |
||||
$and: [options.query, { |
||||
$or: [{ |
||||
'token.apn': { |
||||
$exists: true |
||||
} |
||||
}, { |
||||
'token.gcm': { |
||||
$exists: true |
||||
} |
||||
}] |
||||
}] |
||||
}; |
||||
|
||||
return Push.appCollection.find(query).forEach((app) => { |
||||
if (RocketChat.settings.get('Push_debug')) { |
||||
console.log('Push: send to token', app.token); |
||||
} |
||||
|
||||
if (app.token.apn) { |
||||
return sendPush('apn', app.token.apn, options); |
||||
} |
||||
|
||||
if (app.token.gcm) { |
||||
return sendPush('gcm', app.token.gcm, options); |
||||
} |
||||
}); |
||||
}; |
||||
} |
||||
return Push.enabled = true; |
||||
} |
||||
} |
||||
|
||||
Meteor.startup(function() { |
||||
return configurePush(); |
||||
}); |
||||
@ -1,29 +0,0 @@ |
||||
Accounts.registerLoginHandler (loginRequest) -> |
||||
if not loginRequest.cordova |
||||
return undefined |
||||
|
||||
loginRequest = loginRequest.authResponse |
||||
identity = getIdentity(loginRequest.accessToken) |
||||
|
||||
serviceData = |
||||
accessToken: loginRequest.accessToken |
||||
expiresAt: (+new Date) + (1000 * loginRequest.expiresIn) |
||||
|
||||
whitelisted = ['id', 'email', 'name', 'first_name', 'last_name', 'link', 'username', 'gender', 'locale', 'age_range'] |
||||
|
||||
fields = _.pick(identity, whitelisted) |
||||
_.extend(serviceData, fields) |
||||
|
||||
options = {profile: {}} |
||||
profileFields = _.pick(identity, whitelisted) |
||||
_.extend(options.profile, profileFields) |
||||
|
||||
return Accounts.updateOrCreateUserFromExternalService("facebook", serviceData, options) |
||||
|
||||
|
||||
getIdentity = (accessToken) -> |
||||
try |
||||
return HTTP.get("https://graph.facebook.com/me", {params: {access_token: accessToken}}).data |
||||
|
||||
catch err |
||||
throw _.extend new Error("Failed to fetch identity from Facebook. " + err.message), {response: err.response} |
||||
@ -0,0 +1,38 @@ |
||||
function getIdentity(accessToken) { |
||||
try { |
||||
return HTTP.get('https://graph.facebook.com/me', { |
||||
params: { |
||||
access_token: accessToken |
||||
} |
||||
}).data; |
||||
} catch (error) { |
||||
throw _.extend(new Error(`Failed to fetch identity from Facebook. ${error.message}`), { |
||||
response: error.response |
||||
}); |
||||
} |
||||
} |
||||
|
||||
Accounts.registerLoginHandler(function(loginRequest) { |
||||
if (!loginRequest.cordova) { |
||||
return; |
||||
} |
||||
|
||||
loginRequest = loginRequest.authResponse; |
||||
|
||||
const identity = getIdentity(loginRequest.accessToken); |
||||
const serviceData = { |
||||
accessToken: loginRequest.accessToken, |
||||
expiresAt: Date.now() + 1000 * loginRequest.expiresIn |
||||
}; |
||||
|
||||
const whitelisted = ['id', 'email', 'name', 'first_name', 'last_name', 'link', 'username', 'gender', 'locale', 'age_range']; |
||||
const fields = _.pick(identity, whitelisted); |
||||
const options = { |
||||
profile: {} |
||||
}; |
||||
|
||||
_.extend(serviceData, fields); |
||||
_.extend(options.profile, fields); |
||||
|
||||
return Accounts.updateOrCreateUserFromExternalService('facebook', serviceData, options); |
||||
}); |
||||
Loading…
Reference in new issue