def send_newsletter_task(self: ScheduleOnCommitTask, subject, preview_email, testmode, now_, import_subscribers, tags): """Do user import and newsletter inside a Celery worker process. We carefully split transaction handling to several parts. """ # from celery.contrib import rdb ; rdb.set_trace() request = self.get_request() secrets = get_secrets(request.registry) if not now_: now_ = now() mailing_list = secrets["mailgun.mailing_list"] if preview_email: to = preview_email subject = "[PREVIEW] " + subject else: to = mailing_list newsletter = request.registry.queryAdapter(request, INewsletterGenerator) state = NewsletterState(request) text = "Please see the attached HTML mail." @retryable(tm=request.tm) def render_tx(): """Run HTML rendering in its own transaction, as it most likely reads database.""" return newsletter.render(since=state.get_last_send_timestamp()) html = render_tx() html = premailer.transform(html) from_ = secrets["mailgun.from"] domain = secrets["mailgun.domain"] campaign = now().isoformat() mailgun = Mailgun(request.registry) if import_subscribers: # This may take a looooong time.... logger.info("Importing subscribers") import_all_users(mailgun, request.dbsession, mailing_list, tm=request.tm) logger.info("Sending out newsletter %s %s %s %s %s %s", domain, subject, to, from_, campaign, tags) mailgun.send(domain, to, from_, subject, text, html, campaign, tags=tags) if not preview_email: # Only mark newsletter send if not preview state.set_last_send_timestamp(now_)
def resolve(self, capture_data: Optional[dict]=None): if now() > self.deadline_at: raise ManualConfirmationError("Cannot confirm after deadline.") self.action_taken_at = now() self.state = ManualConfirmationState.resolved self.update_capture_data(capture_data)
def resolve(self, capture_data: Optional[dict] = None): if now() > self.deadline_at: raise ManualConfirmationError("Cannot confirm after deadline.") self.action_taken_at = now() self.state = ManualConfirmationState.resolved self.update_capture_data(capture_data)
def test_is_activated(new_user): """Test if user is activated (completed the email activation).""" assert new_user.is_activated() is False # Set activated_at new_user.activated_at = time.now() assert new_user.is_activated() is True
def create_password_reset_token( self, email) -> Optional[Tuple[object, str, int]]: """Sets password reset token for user. :return: [User, password reset token, token expiration in seconds]. ``None`` if user is disabled or is not email login based. """ user = self.get_by_email(email) assert user, "Got password reset request for non-existing email".format( email) if not self.can_login(user): return None activation_token_expiry_seconds = int( self.registry.settings.get( "websauna.activation_token_expiry_seconds", 24 * 3600)) activation = self.Activation() activation.expires_at = now() + timedelta( seconds=activation_token_expiry_seconds) self.dbsession.add(activation) self.dbsession.flush() user.activation = activation assert user.activation.code, "Could not generate the password reset code" return user, activation.code, activation_token_expiry_seconds
def generate_rss(blog_container: BlogContainer): """Generate RSS feed using rfeed""" request = blog_container.request blog_title = request.registry.settings.get("blog.title") blog_email = request.registry.settings.get("blog.rss_feed_email", "*****@*****.**") items = [] for post_resource in blog_container.get_posts(): post = post_resource.post item = rfeed.Item( title=post.title, link=request.resource_url(post_resource), description="This is the description of the first article", author=blog_email, creator=post.author, guid=rfeed.Guid(str(post.id)), pubDate=post.published_at, extensions=[ContentItem(post_resource)]) items.append(item) feed = rfeed.Feed(title=blog_title, link=request.resource_url(blog_container, "rss"), description="", language="en-US", lastBuildDate=now(), items=items, extensions=[Content()]) return feed
def main(argv=sys.argv): if len(argv) < 3: usage(argv) config_uri = argv[1] request = init_websauna(config_uri) User = get_user_class(request.registry) dbsession = request.dbsession if len(argv) == 4: password = argv[3] else: password = getpass.getpass("Password:"******"Password (again):") if password != password2: sys.exit("Password did not match") with transaction.manager: u = User(email=argv[2], username=argv[2]) u.password = password u.registration_source = "command_line" u.activated_at = now() dbsession.add(u) dbsession.flush() request.registry.notify(UserCreated(request, u)) print("Created user #{}: {}, admin: {}".format(u.id, u.email, u.is_admin()))
def add_object(self, obj): """Flush newly created object to persist storage.""" # Users created through admin are useable right away obj.activated_at = now() super(UserAdd, self).add_object(obj)
def mark_performed(self): """ Incoming: This operation has been registered to database. It may need more confirmations. Outgoing: This operation has been broadcasted to network. It's completion and confirmation might require further network confirmations.""" self.performed_at = now() self.state = CryptoOperationState.pending
class UserFactory(BaseFactory): """Factory for creating dummy Users.""" username = factory.Faker("user_name") email = factory.Faker("email") hashed_password = factory.LazyAttribute(lambda obj: hash_password(obj.password)) activated_at = factory.LazyAttribute(lambda obj: now()) @classmethod def _create(cls, model_class, *args, **kwargs): """Ensure that newly created user can log in.""" user = super()._create(model_class, *args, **kwargs) assert user.can_login() return user class Meta: model = ws_models.User exclude = ("password",) class Params: password = "******" #: Trait for creating admin users admin = factory.Trait( groups=factory.LazyAttribute(lambda obj: [ensure_admin_group_returned()]), )
def create_user(dbsession: Session, registry: Registry, email: str = EMAIL, password: str = PASSWORD, admin: bool = False) -> User: """A helper function to create normal and admin users for tests. :param admin: If True run :py:class:`websauna.system.user.usermixin.SiteCreator` login and set the user to admin group. """ user = User(email=email, password=password) user.user_registration_source = "dummy" dbsession.add(user) dbsession.flush() user.username = user.generate_username() user.activated_at = now() assert user.can_login() # First user, make it admin if admin: site_creator = get_site_creator(registry) site_creator.init_empty_site(dbsession, user) return user
def create(request, username, email, password=None, source="command_line", admin=False): """Create a new site user from command line. :param request: :param username: :param email: :param password: :param source: :param admin: Set this user to admin. The first user is always implicitly admin. :return: """ User = get_user_class(request.registry) dbsession = request.dbsession u = dbsession.query(User).filter_by(email=email).first() if u is not None: return u u = User(email=email, username=username) if password: user_registry = get_user_registry(request) user_registry.set_password(u, password) u.registration_source = source u.activated_at = now() dbsession.add(u) dbsession.flush() request.registry.notify(UserCreated(request, u)) if admin: group = dbsession.query(Group).filter_by(name="admin").one_or_none() group.users.append(u) return u
def create_user(dbsession:Session, registry:Registry, email:str=EMAIL, password:str=PASSWORD, admin:bool=False) -> User: """A helper function to create normal and admin users for tests. :param admin: If True run :py:class:`websauna.system.user.usermixin.SiteCreator` login and set the user to admin group. """ user = User(email=email) if password: hasher = registry.getUtility(IPasswordHasher) user.hashed_password = hasher.hash_password(password) user.user_registration_source = "dummy" dbsession.add(user) dbsession.flush() user.username = user.generate_username() user.activated_at = now() assert user.can_login() # First user, make it admin if admin: site_creator = get_site_creator(registry) site_creator.init_empty_site(dbsession, user) return user
def initialize_object(self, form, appstruct, obj: User): password = appstruct.pop("password") form.schema.objectify(appstruct, obj) hasher = self.request.registry.getUtility(IPasswordHasher) obj.hashed_password = hasher.hash_password(password) # Users created through admin are useable right away, so activate the user obj.activated_at = now()
def reset_password(self, user: UserMixin, password: str): """Reset user password and clear all pending activation issues.""" self.set_password(user, password) if not user.activated_at: user.activated_at = now() self.dbsession.delete(user.activation)
def update_login_data(self, user): request = self.request if not user.last_login_at: e = events.FirstLogin(request, user) request.registry.notify(e) # Update user security details user.last_login_at = now() user.last_login_ip = request.client_addr
def create_blank_user(self, user_model, dbsession, email) -> IUserModel: """Create a new blank user instance as we could not find matching user with the existing details.""" user = user_model(email=email) dbsession.add(user) dbsession.flush() user.username = user.generate_username() user.registration_source = self.provider_id user.activated_at = now() return user
def mark_cancelled(self, error: Optional[str]=None): """This operation cannot be completed. Calling this implies automatic :meth:`reverse` of the operation. """ self.failed_at = now() self.state = CryptoOperationState.cancelled self.other_data["error"] = error self.reverse()
def test_cannot_login_if_not_enable(new_user): """Test if setting enabled to false blocks user from login.""" # Set activated_at new_user.activated_at = time.now() assert new_user.can_login() is True # Disable the user new_user.enabled = False assert new_user.can_login() is False
def mark_cancelled(self, error: Optional[str] = None): """This operation cannot be completed. Calling this implies automatic :meth:`reverse` of the operation. """ self.failed_at = now() self.state = CryptoOperationState.cancelled self.other_data["error"] = error self.reverse()
def remember(self, request, userid, **kw): """ Store a userid in the session.""" request.session[self.userid_key] = userid # Do not lose the original log in timestamp if we get multiple calls to remember() if self.authenticated_at_key not in request.session: request.session[self.authenticated_at_key] = now() return []
def test_can_login(new_user): """Test if user can login. User needs to be enabled and activated. """ assert new_user.can_login() is False # Set activated_at new_user.activated_at = time.now() assert new_user.can_login() is True
def user_auth_details_changes(event: UserAuthSensitiveOperation): """Default logic how to invalidate sessions on user auth detail changes. If you are using different session management model you can install a custom handle. :param event: Incoming event instance """ user = event.user # Update the timestamp which session validation checks on every request user.last_auth_sensitive_operation_at = now()
def activate_user(request, dbsession, user): """Checks to perform when the user becomes a valid user for the first time. If this user has already started sign up process through email we need to cancel that. """ user.activated_at = now() # Cancel any pending email activations if the user chooses the option to use social media login if user.activation: dbsession.delete(user.activation)
def kill_user_sessions(request: Request, user: IUser, operation: str): """Notify session to drop this user. :param request: Pyramid request. :param user: User. :param operation: Operation triggering the killing of user sessions. """ user.last_auth_sensitive_operation_at = now() e = events.UserAuthSensitiveOperation(request, user, operation) request.registry.notify(e, request)
def require_confirmation(cls, user: User, phone_number, timeout=4*3600): assert cls.get_pending_confirmation(user) == None dbsession = Session.object_session(user) confirmation = UserNewPhoneNumberConfirmation() confirmation.user = user confirmation.deadline_at = now() + datetime.timedelta(seconds=timeout) confirmation.require_sms(phone_number) dbsession.add(confirmation) dbsession.flush() return confirmation
def require_confirmation(cls, user: User, phone_number, timeout=4 * 3600): assert cls.get_pending_confirmation(user) == None dbsession = Session.object_session(user) confirmation = UserNewPhoneNumberConfirmation() confirmation.user = user confirmation.deadline_at = now() + datetime.timedelta(seconds=timeout) confirmation.require_sms(phone_number) dbsession.add(confirmation) dbsession.flush() return confirmation
class Params: #: Trait for creating public posts public = factory.Trait( published_at=factory.LazyAttribute(lambda obj: now()) ) #: Trait for creating private posts private = factory.Trait( published_at=None )
def forget(self, request): """User logs out or is forced to be forgotten.""" # Wiggle session keys so that we know when unauthentication happened request.session[self.unauthenticated_at_key] = now() # Remove authenticated_at timestamp so nothing can read it when there is no logged in associated user if self.authenticated_at_key in request.session: del request.session[self.authenticated_at_key] return super().forget(request)
def reset_password(self, user: IUser, password: str): """Reset user password and clear all pending activation issues. :param user: User object, :param password: New password. """ self.set_password(user, password) if not user.activated_at: user.activated_at = now() self.dbsession.delete(user.activation)
def user_auth_details_changes(event:UserAuthSensitiveOperation): """Default logic how to invalidate sessions on user auth detail changes. If you are using different session management model you can install a custom handle. :param event: Incoming event instance """ user = event.user # Update the timestamp which session validation checks on every request user.last_auth_sensitive_operation_at = now()
def check_wallet_creation(request) -> bool: """Check if we have notified this user about wallet creation yet. :return: True if this was a wallet creation event """ user = request.user if not "wallet_creation_notified_at" in request.user.user_data: request.user.user_data["wallet_creation_notified_at"] = now().isoformat() request.registry.notify(WalletCreated(request, user)) return True else: return False
def create_email_activation_token(self, user) -> Tuple[str, int]: """Create activation token for the user to be used in the email :return: Tuple (email activation code, expiration in seconds) """ activation = self.Activation() activation_token_expiry_seconds = int(self.registry.settings.get("websauna.activation_token_expiry_seconds", 24*3600)) activation.expires_at = now() + timedelta(seconds=activation_token_expiry_seconds) self.dbsession.add(activation) self.dbsession.flush() user.activation = activation return [activation.code, activation_token_expiry_seconds]
def change_publish_status(context: PostAdmin.Resource, request: Request): """Change publish status.""" post = context.get_object() if post.published_at: post.published_at = None messages.add(request, kind="info", msg="The post has been retracted.", msg_id="msg-unpublished") else: post.published_at = now() messages.add(request, kind="info", msg="The post has been published.", msg_id="msg-published") # Back to show page return HTTPFound(request.resource_url(context, "show"))
def create_user(dbsession: Session, registry: Registry, email: str = EMAIL, password: str = PASSWORD, admin: bool = False) -> User: """A helper function to create normal and admin users for tests. Example: .. code-block:: python import transaction from websauna.tests.utils import create_user def test_some_stuff(dbsession, registry): with transaction.manager: u = create_user(registry) # Do stuff with new user :param email: User's email address. If inot given use unit testing default. :param password: Password as plain text. If not given use unit testing default. :param admin: If True run :py:class:`websauna.system.user.usermixin.SiteCreator` login and set the user to admin group. """ user = User(email=email) if password: hasher = registry.getUtility(IPasswordHasher) user.hashed_password = hasher.hash_password(password) user.user_registration_source = "dummy" dbsession.add(user) dbsession.flush() user.username = user.generate_username() user.activated_at = now() assert user.can_login() # First user, make it admin if admin: site_creator = get_site_creator(registry) site_creator.init_empty_site(dbsession, user) return user
def mark_seen(stream: Stream, object_id: UUID, activity_type=None): """Mark notifications seen bt user. Call this in the context of target page view function. :param activity_type: Optional type if the same object can have several activities of different types. """ unread = stream.activities.filter(Activity.object_id == object_id, Activity.seen_at == None) if activity_type: unread.filter(Activity.activity_type == activity_type) unread.update(values=dict(seen_at=now()))
def update_login_data(self, user: IUser): """Update last_login_at and last_login_ip on User object. If this is the User first login, trigger FirstLogin event. :param user: User object. """ request = self.request if not user.last_login_at: e = events.FirstLogin(request, user) request.registry.notify(e) # Update user security details user.last_login_at = now() user.last_login_ip = request.client_addr
def send_confirmation(self): phone_number = self.get_target_phone_number() if not phone_number: messages.add(self.request, type="error", msg="You do not have phone number set. Please set a phone number before proceeding.") return context = { "sms_code": self.manual_confirmation.other_data["sms_code"], } sms_text = self.render_sms(context) send_sms(self.request, phone_number, sms_text) self.manual_confirmation.other_data["sms_sent_at"] = now()
def require_confirmation(cls, uco: UserCryptoOperation, timeout=4*3600): """Make a crypto operatation to require a SMS confirmation before it can proceed.""" assert uco.id assert uco.crypto_operation.operation_type == CryptoOperationType.withdraw dbsession = Session.object_session(uco) uco.crypto_operation.state = CryptoOperationState.confirmation_required user = uco.user uwc = UserWithdrawConfirmation() uwc.user = user uwc.user_crypto_operation = uco uwc.deadline_at = now() + datetime.timedelta(seconds=timeout) uwc.require_sms(user.user_data["phone_number"]) dbsession.add(uwc) dbsession.flush() return uwc
def create_user(dbsession: Session, registry: Registry, email: str=EMAIL, password: str=PASSWORD, admin: bool=False) -> User: """A helper function to create normal and admin users for tests. Example: .. code-block:: python import transaction from websauna.tests.utils import create_user def test_some_stuff(dbsession, registry): with transaction.manager: u = create_user(registry) # Do stuff with new user :param email: User's email address. If inot given use unit testing default. :param password: Password as plain text. If not given use unit testing default. :param admin: If True run :py:class:`websauna.system.user.usermixin.SiteCreator` login and set the user to admin group. """ user = User(email=email) if password: hasher = registry.getUtility(IPasswordHasher) user.hashed_password = hasher.hash_password(password) user.user_registration_source = "dummy" dbsession.add(user) dbsession.flush() user.username = user.generate_username() user.activated_at = now() assert user.can_login() # First user, make it admin if admin: site_creator = get_site_creator(registry) site_creator.init_empty_site(dbsession, user) return user
def test_confirm_user_withdraw_timeout(dbsession, eth_network_id, eth_asset_id, user_id, topped_up_user): """User did not reply to withdraw confirmation within the timeout.""" with transaction.manager: uca = dbsession.query(UserCryptoAddress).first() asset = dbsession.query(Asset).get(eth_asset_id) withdraw_op = uca.withdraw(asset, Decimal(5), eth_address_to_bin(TEST_ADDRESS), "Foobar", 1) UserWithdrawConfirmation.require_confirmation(withdraw_op) with transaction.manager: ManualConfirmation.run_timeout_checks(dbsession, now() + timedelta(hours=12)) with transaction.manager: confirmation = dbsession.query(UserWithdrawConfirmation).first() assert confirmation.action_taken_at assert confirmation.state == ManualConfirmationState.timed_out assert confirmation.user_crypto_operation.crypto_operation.state == CryptoOperationState.cancelled assert "error" in confirmation.user_crypto_operation.crypto_operation.other_data
def activate_user_by_email_token(self, token: str): """Get user by a password token issued earlier. Consume any activation token. """ activation = self.dbsession.query(self.Activation).filter(self.Activation.code == token).first() if activation: if activation.is_expired(): return None user = self.get_by_activation(activation) user.activated_at = now() self.dbsession.delete(activation) return user return None
def authenticated(request:Request, user:UserMixin, location:str=None) -> HTTPFound: """Logs in the user. TODO: Make this is a registry component for overriding Sets the auth cookies and redirects to the page defined in horus.login_redirect, which defaults to a view named 'index'. Fills in user last login details. :param request: Current request :param user: User model to log in :param location: Override the redirect page. If none use ``horus.login_redirect`` """ # See that our user model matches one we expect from the configuration registry = request.registry User = get_user_class(registry) assert User assert isinstance(user, User) assert user.id, "Cannot login with invalid user object" if not user.can_login(): raise RuntimeError("Got authenticated() request for disabled user - should not happen") headers = remember(request, user.id) # assert headers, "Authentication backend did not give us any session headers" if not user.last_login_at: e = events.FirstLogin(request, user) request.registry.notify(e) # Update user security details user.last_login_at = now() user.last_login_ip = request.client_addr if not location: location = get_config_route(request, 'horus.login_redirect') messages.add(request, kind="success", msg="You are now logged in.", msg_id="msg-you-are-logged-in") return HTTPFound(location=location, headers=headers)
def create_password_reset_token(self, email) -> Optional[Tuple[object, str, int]]: """Sets password reset token for user. :return: [User, password reset token, token expiration in seconds]. ``None`` if user is disabled or is not email login based. """ user = self.get_by_email(email) assert user, "Got password reset request for non-existing email".format(email) if not self.can_login(user): return None activation_token_expiry_seconds = int(self.registry.settings.get("websauna.activation_token_expiry_seconds", 24*3600)) activation = self.Activation() activation.expires_at = now() + timedelta(seconds=activation_token_expiry_seconds) self.dbsession.add(activation) self.dbsession.flush() user.activation = activation assert user.activation.code, "Could not generate the password reset code" return user, activation.code, activation_token_expiry_seconds
def get_or_create_user(request, email, password): User = get_user_class(request.registry) dbsession = request.dbsession u = dbsession.query(User).filter_by(email=email).first() if u is not None: return u u = User(email=email, username=email) dbsession.add(u) dbsession.flush() # Make sure u.user_data is set if password: user_registry = get_user_registry(request) user_registry.set_password(u, password) u.registration_source = "command_line" u.activated_at = now() request.registry.notify(UserCreated(request, u)) return u
def test_forget_password_expired_token(web_server, browser, dbsession, init): """Reset password by email.""" with transaction.manager: create_user(dbsession, init.config.registry) b = browser b.visit(web_server + "/forgot-password") assert b.is_element_present_by_css("#forgot-password-form") b.fill("email", EMAIL) b.find_by_name("submit").click() assert b.is_element_present_by_css("#msg-check-email") with transaction.manager: user = get_user(dbsession) activation = user.activation activation.expires_at = now() - timedelta(days=365) activation_code = activation.code b.visit("{}/reset-password/{}".format(web_server, activation_code)) assert b.is_element_present_by_css("#not-found")
def create_session(request): session = session_factory(request) if "created_at" not in session: session["created_at"] = now() return session
def is_recent(self): return self.published_at >= now() - datetime.timedelta(days=1)
def is_expired(self): """The activation best before is past and we should not use it anymore.""" return self.expires_at < now()
def remember(self, request, userid, **kw): """ Store a userid in the session.""" request.session[self.userid_key] = userid request.session[self.created_at_key] = now() return []
def kill_user_sessions(request, user, operation): # Notify session to drop this user user.last_auth_sensitive_operation_at = now() e = events.UserAuthSensitiveOperation(request, user, operation) request.registry.notify(e)