Example #1
0
	requestCredential(options, credentialRequestCompleteCallback) {
		// support both (options, callback) and (callback).
		if (!credentialRequestCompleteCallback && typeof options === 'function') {
			credentialRequestCompleteCallback = options;
			options = {};
		}

		const config = ServiceConfiguration.configurations.findOne({ service: this.name });
		if (!config) {
			if (credentialRequestCompleteCallback) {
				credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError());
			}
			return;
		}

		const credentialToken = Random.secret();
		const loginStyle = OAuth._loginStyle(this.name, config, options);

		const separator = this.authorizePath.indexOf('?') !== -1 ? '&' : '?';

		const loginUrl = `${ this.authorizePath
		}${ separator }client_id=${ config.clientId
		}&redirect_uri=${ OAuth._redirectUri(this.name, config)
		}&response_type=code` +
			`&state=${ OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl)
			}&scope=${ this.scope }`;

		OAuth.launchLogin({
			loginService: this.name,
			loginStyle,
			loginUrl,
			credentialRequestCompleteCallback,
			credentialToken,
			popupOptions: {
				width: 900,
				height: 450,
			},
		});
	}
Example #2
0
  Accounts.onCreateUser((options, user) => {
    const shopId = Reaction.getShopId(); // current shop; not primary shop
    const groupToAddUser = options.groupId;
    const roles = {};
    const additionals = {
      name: options && options.name,
      profile: Object.assign({}, options && options.profile)
    };
    if (!user.emails) user.emails = [];
    // init default user roles
    // we won't create users unless we have a shop.
    if (shopId) {
      // retain language when user has defined a language
      // perhaps should be treated as additionals
      // or in onLogin below, or in the anonymous method options
      if (!(Meteor.users.find().count() === 0)) { // dont set on inital admin
        if (!user.profile) user.profile = {};
        const currentUser = Meteor.user(user);
        if (currentUser && currentUser.profile) {
          if (currentUser.profile.lang && !user.profile.lang) {
            user.profile.lang = currentUser.profile.lang;
          }
          if (currentUser.profile.currency && !user.profile.currency) {
            user.profile.currency = currentUser.profile.currency;
          }
        }
      }
      // if we don't have user.services we're an anonymous user
      if (!user.services) {
        const group = Collections.Groups.findOne({ slug: "guest", shopId });
        const defaultGuestRoles = group.permissions;
        // if no defaultGuestRoles retrieved from DB, use the default Reaction set
        roles[shopId] = defaultGuestRoles || Reaction.defaultVisitorRoles;
        additionals.groups = [group._id];
      } else {
        let group;
        if (groupToAddUser) {
          group = Collections.Groups.findOne({ _id: groupToAddUser, shopId });
        } else {
          group = Collections.Groups.findOne({ slug: "customer", shopId });
        }
        // if no group or customer permissions retrieved from DB, use the default Reaction customer set
        roles[shopId] = group.permissions || Reaction.defaultCustomerRoles;
        additionals.groups = [group._id];
        // also add services with email defined to user.emails[]
        const userServices = user.services;
        for (const service in userServices) {
          if ({}.hasOwnProperty.call(userServices, service)) {
            const serviceObj = userServices[service];
            if (serviceObj.email) {
              const email = {
                provides: "default",
                address: serviceObj.email,
                verified: true
              };
              user.emails.push(email);
            }
            if (serviceObj.name) {
              user.username = serviceObj.name;
              additionals.profile.name = serviceObj.name;
            }
            // TODO: For now we have here instagram, twitter and google avatar cases
            // need to make complete list
            if (serviceObj.picture) {
              additionals.profile.picture = user.services[service].picture;
            } else if (serviceObj.profile_image_url_https) {
              additionals.profile.picture = user.services[service].dprofile_image_url_https;
            } else if (serviceObj.profile_picture) {
              additionals.profile.picture = user.services[service].profile_picture;
            }
            // Correctly map Instagram profile data to Meteor user / Accounts
            if (userServices.instagram) {
              user.username = serviceObj.username;
              user.name = serviceObj.full_name;
              additionals.name = serviceObj.full_name;
              additionals.profile.picture = serviceObj.profile_picture;
              additionals.profile.bio = serviceObj.bio;
              additionals.profile.name = serviceObj.full_name;
              additionals.profile.username = serviceObj.username;
            }
          }
        }
      }

      // clone before adding roles
      const account = Object.assign({}, user, additionals);
      account.userId = user._id;
      Collections.Accounts.insert(account);
      Hooks.Events.run("afterAccountsInsert", account.userId, user._id);

      const userDetails = Collections.Accounts.findOne({ _id: user._id });

      // send a welcome email to new users,
      // but skip the first default admin user and anonymous users
      // (default admins already get a verification email)
      if (userDetails.emails && userDetails.emails.length > 0
        && (!(Meteor.users.find().count() === 0) && !userDetails.profile.invited)) {
        const token = Random.secret();
        Meteor.call("accounts/sendWelcomeEmail", shopId, user._id, token);
        const defaultEmail = userDetails.emails.find((email) => email.provides === "default");
        const when = new Date();
        const tokenObj = {
          address: defaultEmail.address,
          token,
          when
        };
        _.set(user, "services.email.verificationTokens", [tokenObj]);
      }

      // assign default user roles
      user.roles = roles;

      // run onCreateUser hooks
      // (the user object must be returned by all callbacks)
      const userDoc = Hooks.Events.run("onCreateUser", user, options);
      return userDoc;
    }
  });
Example #3
0
export async function sendUpdatedVerificationEmail(userId, email) {
  // Make sure the user exists, and email is one of their addresses.
  const user = Meteor.users.findOne(userId);

  if (!user) {
    Logger.error("sendVerificationEmail - User not found");
    throw new Meteor.Error("not-found", "User not found");
  }

  let address = email;

  // pick the first unverified address if no address provided.
  if (!email) {
    const unverifiedEmail = _.find(user.emails || [], (e) => !e.verified) || {};

    ({ address } = unverifiedEmail);

    if (!address) {
      const msg = "No unverified email addresses found.";
      Logger.error(msg);
      throw new Meteor.Error("not-found", msg);
    }
  }

  // make sure we have a valid address
  if (!address || !user.emails || !(user.emails.map((mailInfo) => mailInfo.address).includes(address))) {
    const msg = "Email not found for user";
    Logger.error(msg);
    throw new Meteor.Error("not-found", msg);
  }

  const token = Random.secret();
  const when = new Date();
  const tokenObj = { token, address, when };

  Meteor.users.update({ _id: userId }, {
    $push: {
      "services.email.verificationTokens": tokenObj
    }
  });

  const shopName = Reaction.getShopName();
  const url = Accounts.urls.verifyEmail(token);
  const copyrightDate = new Date().getFullYear();

  const dataForEmail = {
    // Reaction Information
    contactEmail: "*****@*****.**",
    homepage: Meteor.absoluteUrl(),
    emailLogo: `${Meteor.absoluteUrl()}resources/placeholder.gif`,
    copyrightDate,
    legalName: "Reaction Commerce",
    physicalAddress: {
      address: "2110 Main Street, Suite 207",
      city: "Santa Monica",
      region: "CA",
      postal: "90405"
    },
    shopName,
    socialLinks: {
      facebook: {
        link: "https://www.facebook.com/reactioncommerce"
      },
      github: {
        link: "https://github.com/reactioncommerce/reaction"
      },
      instagram: {
        link: "https://instagram.com/reactioncommerce"
      },
      twitter: {
        link: "https://www.twitter.com/getreaction"
      }
    },
    confirmationUrl: url,
    userEmailAddress: address
  };

  if (!Reaction.Email.getMailUrl()) {
    Logger.warn(`

  ***************************************************
          IMPORTANT! EMAIL VERIFICATION LINK

           Email sending is not configured.

  Go to the following URL to verify email: ${address}

  ${url}
  ***************************************************

    `);
  }

  const tpl = "accounts/verifyUpdatedEmail";
  const subject = "accounts/verifyUpdatedEmail/subject";

  SSR.compileTemplate(tpl, Reaction.Email.getTemplate(tpl));
  SSR.compileTemplate(subject, Reaction.Email.getSubject(tpl));

  return Reaction.Email.send({
    to: address,
    from: Reaction.getShopEmail(),
    subject: SSR.render(subject, dataForEmail),
    html: SSR.render(tpl, dataForEmail)
  });
}
Example #4
0
export async function sendResetPasswordEmail(userId, optionalEmail) {
  // Make sure the user exists, and email is one of their addresses.
  const user = Meteor.users.findOne(userId);

  if (!user) {
    Logger.error("sendResetPasswordEmail - User not found");
    throw new Meteor.Error("not-found", "User not found");
  }

  let email = optionalEmail;

  // pick the first email if we weren't passed an email.
  if (!optionalEmail && user.emails && user.emails[0]) {
    email = user.emails[0].address;
  }

  // make sure we have a valid email
  if (!email || !user.emails || !user.emails.map((mailInfo) => mailInfo.address).includes(email)) {
    Logger.error("sendResetPasswordEmail - Email not found");
    throw new Meteor.Error("not-found", "Email not found");
  }

  // Create token for password reset
  const token = Random.secret();
  const when = new Date();
  const tokenObj = { token, email, when };

  Meteor.users.update(userId, {
    $set: {
      "services.password.reset": tokenObj
    }
  });

  Meteor._ensure(user, "services", "password").reset = tokenObj;

  // Get shop data for email display
  const shop = Shops.findOne(Reaction.getShopId());
  const emailLogo = Reaction.Email.getShopLogo(shop);
  const copyrightDate = new Date().getFullYear();

  const dataForEmail = {
    // Shop Data
    shop,
    contactEmail: shop.emails[0].address,
    homepage: Meteor.absoluteUrl(),
    emailLogo,
    copyrightDate,
    legalName: _.get(shop, "addressBook[0].company"),
    physicalAddress: {
      address: `${_.get(shop, "addressBook[0].address1")} ${_.get(shop, "addressBook[0].address2")}`,
      city: _.get(shop, "addressBook[0].city"),
      region: _.get(shop, "addressBook[0].region"),
      postal: _.get(shop, "addressBook[0].postal")
    },
    shopName: shop.name,
    socialLinks: {
      display: true,
      facebook: {
        display: true,
        icon: `${Meteor.absoluteUrl()}resources/email-templates/facebook-icon.png`,
        link: "https://www.facebook.com"
      },
      googlePlus: {
        display: true,
        icon: `${Meteor.absoluteUrl()}resources/email-templates/google-plus-icon.png`,
        link: "https://plus.google.com"
      },
      twitter: {
        display: true,
        icon: `${Meteor.absoluteUrl()}resources/email-templates/twitter-icon.png`,
        link: "https://www.twitter.com"
      }
    },
    // Account Data
    passwordResetUrl: Accounts.urls.resetPassword(token),
    user
  };

  // Compile Email with SSR
  const tpl = "accounts/resetPassword";
  const subject = "accounts/resetPassword/subject";
  SSR.compileTemplate(tpl, Reaction.Email.getTemplate(tpl));
  SSR.compileTemplate(subject, Reaction.Email.getSubject(tpl));

  return Reaction.Email.send({
    to: email,
    from: Reaction.getShopEmail(),
    subject: SSR.render(subject, dataForEmail),
    html: SSR.render(tpl, dataForEmail)
  });
}
Example #5
0
	_.each(entry.body.recipients, (recipientId) => {
		if (!concluded[recipientId]) {
			var mail = null;
			var unsubToken = null;

			try {
				const user = Meteor.users.findOne(recipientId);

				if (!user) {
					throw "User not found for ID '" + recipientId + "'";
				}

				if (user.notifications === false) {
					throw "User wishes to not receive notifications";
				}

				if (!user.emails || !user.emails[0] || !user.emails[0].address) {
					throw "Recipient has no email address registered";
				}

				var	email = user.emails[0];
				var address = email.address;

				var username = user.username;
				var userLocale = user.profile && user.profile.locale || 'en';

				var siteName = Accounts.emailTemplates.siteName;
				var subjectPrefix = '['+siteName+'] ';

				unsubToken = Random.secret();

				var vars = model.vars(userLocale, user);

				const fromAddress = vars.fromAddress
				                 || Accounts.emailTemplates.from;

				vars.unsubLink = Router.url('profile.unsubscribe', { token: unsubToken });
				vars.siteName = siteName;
				vars.locale = userLocale;
				vars.username = username;
				vars.logo = logo('mails/logo.png');

				var message = SSR.render(model.template, vars);

				// Template can't handle DOCTYPE header, so we add the thing here.
				var DOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
				message = DOCTYPE + message;

				mail =
					{ from: fromAddress
					, sender: Accounts.emailTemplates.from
					, to: address
					, subject: subjectPrefix + vars.subject
					, html: message
					, attachments: [ vars.logo.attachement ]
					};

				Email.send(mail);

				Notification.SendResult.record(entry, unsubToken, true, recipientId, mail, "success");
			}
			catch(e) {
				var reason = e;
				if (typeof e == 'object' && 'toJSON' in e) reason = e.toJSON();
				Notification.SendResult.record(entry, unsubToken, false, recipientId, mail, reason);
			}
		}
	});
Example #6
0
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { Accounts } from 'meteor/accounts-base';

Meteor.methods({
	'personalAccessTokens:generateToken'({ tokenName }) {
		if (!Meteor.userId()) {
			throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:generateToken' });
		}
		if (!RocketChat.authz.hasPermission(Meteor.userId(), 'create-personal-access-tokens')) {
			throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'personalAccessTokens:generateToken' });
		}

		const token = Random.secret();
		const tokenExist = RocketChat.models.Users.findPersonalAccessTokenByTokenNameAndUserId({
			userId: Meteor.userId(),
			tokenName,
		});
		if (tokenExist) {
			throw new Meteor.Error('error-token-already-exists', 'A token with this name already exists', { method: 'personalAccessTokens:generateToken' });
		}

		RocketChat.models.Users.addPersonalAccessTokenToUser({
			userId: Meteor.userId(),
			loginTokenObject: {
				hashedToken: Accounts._hashLoginToken(token),
				type: 'personalAccessToken',
				createdAt: new Date(),
				lastTokenPart: token.slice(-6),
				name: tokenName,
			},
Example #7
0
export function sendResetPasswordEmail(userId, optionalEmail) {
  // Make sure the user exists, and email is one of their addresses.
  const user = Meteor.users.findOne(userId);

  if (!user) {
    Logger.error("sendResetPasswordEmail - User not found");
    throw new Meteor.Error("user-not-found", "User not found");
  }

  let email = optionalEmail;

  // pick the first email if we weren't passed an email.
  if (!optionalEmail && user.emails && user.emails[0]) {
    email = user.emails[0].address;
  }

  // make sure we have a valid email
  if (!email || !user.emails || !user.emails.map((mailInfo) => mailInfo.address).includes(email)) {
    Logger.error("sendResetPasswordEmail - Email not found");
    throw new Meteor.Error("email-not-found", "Email not found");
  }

  // Create token for password reset
  const token = Random.secret();
  const when = new Date();
  const tokenObj = { token, email, when };

  Meteor.users.update(userId, {
    $set: {
      "services.password.reset": tokenObj
    }
  });

  Meteor._ensure(user, "services", "password").reset = tokenObj;

  // Get shop data for email display
  const shop = Shops.findOne(Reaction.getShopId());

  // Get shop logo, if available. If not, use default logo from file-system
  let emailLogo;
  if (Array.isArray(shop.brandAssets)) {
    const brandAsset = _.find(shop.brandAssets, (asset) => asset.type === "navbarBrandImage");
    const mediaId = Media.findOne(brandAsset.mediaId);
    emailLogo = path.join(Meteor.absoluteUrl(), mediaId.url());
  } else {
    emailLogo = Meteor.absoluteUrl() + "resources/email-templates/shop-logo.png";
  }

  const dataForEmail = {
    // Shop Data
    shop: shop,
    contactEmail: shop.emails[0].address,
    homepage: Meteor.absoluteUrl(),
    emailLogo: emailLogo,
    copyrightDate: moment().format("YYYY"),
    legalName: shop.addressBook[0].company,
    physicalAddress: {
      address: shop.addressBook[0].address1 + " " + shop.addressBook[0].address2,
      city: shop.addressBook[0].city,
      region: shop.addressBook[0].region,
      postal: shop.addressBook[0].postal
    },
    shopName: shop.name,
    socialLinks: {
      display: true,
      facebook: {
        display: true,
        icon: Meteor.absoluteUrl() + "resources/email-templates/facebook-icon.png",
        link: "https://www.facebook.com"
      },
      googlePlus: {
        display: true,
        icon: Meteor.absoluteUrl() + "resources/email-templates/google-plus-icon.png",
        link: "https://plus.google.com"
      },
      twitter: {
        display: true,
        icon: Meteor.absoluteUrl() + "resources/email-templates/twitter-icon.png",
        link: "https://www.twitter.com"
      }
    },
    // Account Data
    passwordResetUrl: Accounts.urls.resetPassword(token),
    user: user
  };

  // Compile Email with SSR
  const tpl = "accounts/resetPassword";
  const subject = "accounts/resetPassword/subject";
  SSR.compileTemplate(tpl, Reaction.Email.getTemplate(tpl));
  SSR.compileTemplate(subject, Reaction.Email.getSubject(tpl));

  return Reaction.Email.send({
    to: email,
    from: Reaction.getShopEmail(),
    subject: SSR.render(subject, dataForEmail),
    html: SSR.render(tpl, dataForEmail)
  });
}
Example #8
0
Template.sqrlButtons.onCreated(function sqrlButtonsOnCreated() {
  this.sqrlNonce = Random.secret();
});
Example #9
0
File: core.js Project: SunFed/store
    let options = {};
    options = Hooks.Events.run("beforeCreateDefaultAdminUser", options);

    // If $REACTION_SECURE_DEFAULT_ADMIN is set to "true" on first run,
    // a random email/password will be generated instead of using the
    // default email and password (email: admin@localhost pw: r3@cti0n)
    // and the new admin user will need to verify their email to log in.
    // If a random email and password are generated, the console will be
    // the only place to retrieve them.
    // If the admin email/password is provided via environment or Meteor settings,
    // the $REACTION_SECURE_DEFAULT_ADMIN will only enforce the email validation part.
    const isSecureSetup = process.env.REACTION_SECURE_DEFAULT_ADMIN === "true";

    // generate default values to use if none are supplied
    const defaultEmail = isSecureSetup ? `${Random.id(8).toLowerCase()}@localhost` : "admin@localhost";
    const defaultPassword = isSecureSetup ? Random.secret(8) : "r3@cti0n";
    const defaultUsername = "******";
    const defaultName = "Admin";

    // Process environment variables and Meteor settings for initial user config.
    // If ENV variables are set, they always override Meteor settings (settings.json).
    // This is to allow for testing environments where we don't want to use users configured in a settings file.
    const env = process.env;
    let configureEnv = false;

    if (env.REACTION_EMAIL && env.REACTION_AUTH) {
      configureEnv = true;
      Logger.info("Using environment variables to create admin user");
    }

    // defaults use either env or generated values