def test_move(self):
     'Make sure that only superusers can promote or demote other users'
     url = self.get_url('user_move')
     # Check that we only see the login page if the user is not logged in
     self.assert_('value=Login' in self.post(url).unicode_body)
     # Check that normal users can't promote or demote other users
     self.login(self.userN)
     token = get_token(self.get(self.get_url('user_index')).body)
     targetUser = db.query(User).filter_by(username=self.userS['username']).first()
     params = ReplaceableDict(token=token, targetUserID=targetUser.id, is_super=1)
     self.assert_('value=Login' in self.post(url, params.replace(is_super=0)))
     self.assert_('value=Login' in self.post(url, params.replace(is_super=1)))
     # Prepare
     self.login(self.userS)
     user = db.query(User).filter_by(username=self.userS['username']).first()
     token = get_token(self.get(self.get_url('user_index')).body)
     targetUser = db.query(User).filter_by(username=self.userN['username']).first()
     params = ReplaceableDict(token=token, targetUserID=targetUser.id, is_super=1)
     # Check that a bad token fails
     self.assertJSON(self.post(url, params.replace(token=token + 'x')), 0)
     # Check that a bad targetUserID fails
     self.assertJSON(self.post(url, params.replace(targetUserID=0)), 0)
     # Check that a bad is_super fails
     self.assertJSON(self.post(url, params.replace(is_super='xxx')), 0)
     # Check that a super user cannot promote or demote self
     self.assertJSON(self.post(url, params.replace(targetUserID=user.id, is_super=0)), 0)
     self.assertJSON(self.post(url, params.replace(targetUserID=user.id, is_super=1)), 0)
     # Check that a super user can promote or demote other users
     self.assertJSON(self.post(url, params.replace(is_super=1)), 1)
     self.assertJSON(self.post(url, params.replace(is_super=0)), 1)
def update_(request):
    'Update account'
    params = request.params
    if params.get('token') != request.session.get_csrf_token():
        return dict(isOk=0, message='Invalid token')
    userID = authenticated_userid(request)
    # If the user is trying to update account information, send confirmation email
    if 'username' in params:
        return save_user_(request, dict(params), 'update', db.query(User).get(userID))
    # Load
    smsAddressAction = params.get('smsAddressAction')
    # If the user is adding an SMS address,
    if 'add' == smsAddressAction:
        # Make sure it is a valid email address
        validateEmail = validators.Email().to_python
        try:
            smsAddressEmail = validateEmail(params.get('smsAddressEmail', ''))
        except Invalid, error:
            return dict(isOk=0, message=str(error))
        # Check for duplicates
        smsAddress = db.query(SMSAddress).filter(
            (SMSAddress.email == smsAddressEmail) & 
            (SMSAddress.user_id == userID)).first()
        if smsAddress:
            return dict(isOk=0, message='You already added this SMS address')
        # Add it to the database
        smsAddress = SMSAddress(email=smsAddressEmail, user_id=userID, code=make_random_string(CODE_LEN))
        db.add(smsAddress)
        # Send confirmation code
        get_mailer(request).send_to_queue(Message(
            recipients=[smsAddress.email],
            body=smsAddress.code))
        # Return smsAddresses
        return dict(isOk=1, content=render('users/smsAddresses.mak', update(request), request))
def index(request):
    'Show registered IMAP accounts'
    imapAccountQuery = db.query(IMAPAccount).options(joinedload(IMAPAccount.user))
    personQuery = db.query(User.id, User.nickname)
    # statements = [Session.query(model.IMAPMessage.imap_account_id, sa.func.count('*').label('message_count')).filter(model.IMAPMessage.message_status>model.message_incomplete).group_by(model.IMAPMessage.imap_account_id).subquery()]
    # columns = [model.IMAPAccount] + [x.c.message_count for x in statements]
    # imapAccountQuery = Session.query(*columns).options(orm.eagerload(model.IMAPAccount.owner))
    # for statement in statements:
        # imapAccountQuery = imapAccountQuery.outerjoin((statement, model.IMAPAccount.id==statement.c.imap_account_id))
    return dict(imapAccountPacks=imapAccountQuery.all(), personPacks=personQuery.all())
 def test_reset(self):
     'Make sure that resetting the password works'
     url = self.get_url('user_reset')
     email = self.userN['email']
     password_ = hash(self.userN['password'])
     # Trying to reset an email that does not exist returns an error
     self.assertJSON(self.post(url, dict(email=email + 'x')), 0)
     # Resetting the password does not immediately change the password
     self.assertJSON(self.post(url, dict(email=email)), 1)
     self.assertEqual(db.query(User).filter_by(email=email, password_=password_).count(), 1)
     # Apply change
     self.get(self.get_url('user_confirm', ticket=db.query(User_.ticket).filter_by(email=email).order_by(User_.when_expired.desc()).first()[0]))
     self.assertEqual(db.query(User).filter_by(email=email, password_=password_).count(), 0)
 def _to_python(self, value, user):
     'Check whether the value is unique'
     # If the user is new or the value changed,
     if not user or getattr(user, self.fieldName) != value:
         # Make sure the value is unique
         if db.query(User).filter(getattr(User, self.fieldName)==value).first():
             # Raise
             raise Invalid(self.errorMessage, value, user)
     # Return
     return value
    def test_update_smsAddress(self):
        'Make sure that updating smsAddresses works'
        url = self.get_url('user_update')
        # Get token
        self.login(self.userN)
        token = get_token(self.get(url).unicode_body)

        params = ReplaceableDict(token=token, smsAddressAction='add', smsAddressEmail='*****@*****.**')
        # Add an smsAddress that is not a valid email address
        self.assertJSON(self.post(url, params.replace(smsAddressEmail='xxx')), 0)
        # Add an smsAddress that is a valid email address
        self.login(self.userN)
        smsAddressEmail = '*****@*****.**'
        self.assertJSON(self.post(url, params.replace(smsAddressEmail=smsAddressEmail)), 1)
        smsAddress1 = db.query(SMSAddress).filter_by(email=smsAddressEmail).first()
        self.login(self.userS)
        smsAddressEmail = '*****@*****.**'
        self.assertJSON(self.post(url, params.replace(smsAddressEmail=smsAddressEmail)), 1)
        smsAddress2 = db.query(SMSAddress).filter_by(email=smsAddressEmail).first()
        # Add a duplicate smsAddress
        self.assertJSON(self.post(url, params), 1)
        self.assertJSON(self.post(url, params), 0)

        params = ReplaceableDict(token=token, smsAddressAction='activate', smsAddressID=smsAddress1.id, smsAddressCode=smsAddress1.code)
        self.login(self.userN)
        # Activate an smsAddress that doesn't belong to the user
        self.assertJSON(self.post(url, params.replace(smsAddressID=smsAddress2.id, smsAddressCode=smsAddress2.code)), 0)
        # Activate an smsAddress with a bad code
        self.assertJSON(self.post(url, params.replace(smsAddressCode=smsAddress1.code + 'x')), 0)
        # Activate an smsAddress with a good code
        self.assertJSON(self.post(url, params), 1)

        params = ReplaceableDict(token=token, smsAddressAction='remove', smsAddressID=smsAddress1.id)
        # Remove an smsAddress that doesn't belong to the user
        self.assertJSON(self.post(url, params.replace(smsAddressID=smsAddress2.id)), 0)
        # Remove an smsAddress that does belong to the user
        self.assertJSON(self.post(url, params), 1)

        params = ReplaceableDict(token=token)
        # Send an invalid command
        self.assertJSON(self.post(url, params.replace(smsAddressAction='')), 0)
        self.assertJSON(self.post(url, params.replace(smsAddressAction='xxx')), 0)
 def test_registration(self):
     'Make sure that registration works'
     url = self.get_url('user_register')
     username, password, nickname, email = [self.userN[x].replace('2', '3') for x in 'username', 'password', 'nickname', 'email']
     params = ReplaceableDict(username=username, password=password, nickname=nickname, email=email)
     # Make sure the registration page appears properly
     self.assert_('Registration' in self.get(url).unicode_body)
     # Register
     self.post(url, params)
     # Register with the same username but with different case
     self.post(url, params.replace(username=username.upper(), nickname=nickname + 'x', email=email + 'x'))
     # Register with the same nickname but with different case
     self.post(url, params.replace(username=username + 'x', nickname=nickname.upper(), email=email + 'x'))
     # Register with the same email but with different case
     self.post(url, params.replace(username=username + 'x', nickname=nickname + 'x', email=email.upper()))
     # Confirm registration
     self.get(self.get_url('user_confirm', ticket=db.query(User_.ticket).filter_by(email=email).order_by(User_.when_expired.desc()).first()[0]))
     # Make sure the user exists
     self.assertEqual(db.query(User).filter_by(email=email).count(), 1)
     # Make sure that conflicting registrations have been deleted
     self.assertEqual(db.query(User_).filter_by(password_=hash(password)).count(), 0)
 def test_update(self):
     'Make sure that updating credentials works'
     url = self.get_url('user_update')
     # Check that we only see the login page if the user is not logged in
     self.assert_('value=Login' in self.get(url).unicode_body)
     self.assert_('value=Login' in self.post(url).unicode_body)
     # Check that the update form is filled with the user's credentials
     self.login(self.userN)
     body = self.get(url).unicode_body
     self.assert_(self.userN['username'] in body)
     self.assert_(self.userN['nickname'] in body)
     self.assert_(self.userN['email'].lower() in body)
     token = get_token(body)
     # Updating credentials requires a token
     username, password, nickname, email = ['0' + self.userN[x] for x in 'username', 'password', 'nickname', 'email']
     password_ = hash(password)
     params = ReplaceableDict(token=token, username=username, password=password, nickname=nickname, email=email)
     self.assertJSON(self.post(url, params.replace(token='')), 0)
     self.assertJSON(self.post(url, params), 1)
     # Make sure the credentials have not changed yet
     self.assertEqual(db.query(User).filter_by(username=username, password_=password_, nickname=nickname, email=email).count(), 0)
     # Make sure the credentials have changed after confirmation
     self.get(self.get_url('user_confirm', ticket=db.query(User_.ticket).filter_by(email=email).order_by(User_.when_expired.desc()).first()[0]))
     self.assertEqual(db.query(User).filter_by(username=username, password_=password_, nickname=nickname, email=email).count(), 1)
def reset(request):
    'Reset password'
    # Get email
    email = request.params.get('email')
    # Try to load the user
    user = db.query(User).filter(User.email==email).first()
    # If the email is not in our database,
    if not user: 
        return dict(isOk=0)
    # Reset account
    return save_user_(request, dict(
        username=user.username, 
        password=make_random_string(PASSWORD_LEN_MAX),
        nickname=user.nickname,
        email=user.email), 'reset', user)
def add(request):
    'Add IMAP account'
    params = request.params
    if params.get('token') != request.session.get_csrf_token():
        return dict(isOk=0, message='Invalid token')
    accountUserID = params.get('accountUserID', 0)
    accountHost = params.get('accountHost', '')
    accountUsername = params.get('accountUsername', '')
    accountPassword = params.get('accountPassword', '')
    # Check user
    if not db.query(User).get(accountUserID):
        return dict(isOk=0, message='Could not find accountUserID=%s' % accountUserID)
    # Check account credentials using validators
    try:
        form = IMAPAccountForm().to_python(params)
    except Invalid, error:
        return dict(isOk=0, errorByID=error.unpack_errors())
def apply_user_(ticket):
    'Finalize a change to a user account'
    # Load
    user_ = db.query(User_).filter(
        (User_.ticket == ticket) & 
        (User_.when_expired >= datetime.datetime.utcnow())).first()
    # If the ticket is valid,
    if user_:
        # Apply the change and reset rejection_count
        db.merge(User(
            id=user_.user_id,
            username=user_.username,
            password_=user_.password_,
            nickname=user_.nickname,
            email=user_.email,
            rejection_count=0))
    # Return
    return user_
def move(request):
    'Move a user to a different group'
    params = request.params
    if params.get('token') != request.session.get_csrf_token():
        return dict(isOk=0, message='Invalid token')
    userID = authenticated_userid(request)
    # Load
    targetUserID = params.get('targetUserID', 0)
    targetUser = db.query(User).get(targetUserID)
    is_super = params.get('is_super', 0)
    if not targetUser:
        return dict(isOk=0, message='Could not find targetUserID=%s' % targetUserID)
    if int(userID) == int(targetUserID):
        return dict(isOk=0, message='Cannot promote or demote yourself')
    try:
        is_super = bool(int(is_super))
    except ValueError:
        return dict(isOk=0, message='Could not parse is_super=%s' % is_super)
    targetUser.is_super = is_super
    return dict(isOk=1)
def login_(request):
    'Process login credentials'
    # Make shortcuts
    environ, params, registry = [getattr(request, x) for x in 'environ', 'params', 'registry']
    username, password = [params.get(x, '').strip() for x in 'username', 'password']
    if not username or not password:
        return dict(isOk=0)
    # Check username
    user = db.query(User).filter_by(username=username).first()
    if not user:
        return dict(isOk=0)
    # If the password is incorrect, increment and return rejection_count
    if user.password_ != hash(password):
        user.rejection_count += 1
        return dict(isOk=0, rejection_count=user.rejection_count)
    # If there have been too many rejections, expect recaptcha
    if user.rejection_count >= REJECTION_LIMIT:
        rChallenge, rResponse = [params.get(x, '') for x in 'recaptcha_challenge', 'recaptcha_response']
        rPrivate = registry.settings.get('recaptcha.private', '')
        clientIP = environ.get('HTTP_X_REAL_IP', environ.get('HTTP_X_FORWARDED_FOR', environ.get('REMOTE_ADDR')))
        # If the response is not valid, say so
        if not captcha.submit(rChallenge, rResponse, rPrivate, clientIP).is_valid:
            return dict(isOk=0, rejection_count=user.rejection_count)
    # Save user
    try:
        user.offset = int(params.get('offset', MINUTES_OFFSET))
    except ValueError:
        user.offset = MINUTES_OFFSET
    user.when_login = datetime.datetime.utcnow()
    user.rejection_count = 0
    # Set headers to set cookie
    if not hasattr(request, 'response_headerlist'):
        request.response_headerlist = []
    request.response_headerlist.extend(remember(request, user.id, tokens=format_tokens(user)))
    # Return
    return dict(isOk=1)
         (SMSAddress.user_id == userID)).first()
     if smsAddress:
         return dict(isOk=0, message='You already added this SMS address')
     # Add it to the database
     smsAddress = SMSAddress(email=smsAddressEmail, user_id=userID, code=make_random_string(CODE_LEN))
     db.add(smsAddress)
     # Send confirmation code
     get_mailer(request).send_to_queue(Message(
         recipients=[smsAddress.email],
         body=smsAddress.code))
     # Return smsAddresses
     return dict(isOk=1, content=render('users/smsAddresses.mak', update(request), request))
 # Make sure the smsAddressID belongs to the user
 smsAddressID = params.get('smsAddressID')
 smsAddress = db.query(SMSAddress).filter(
     (SMSAddress.id == smsAddressID) & 
     (SMSAddress.user_id == userID)).first()
 if not smsAddress:
     return dict(isOk=0, message='Could not find smsAddressID=%s corresponding to userID=%s' % (smsAddressID, userID))
 # If the user is activating an SMS address,
 if 'activate' == smsAddressAction:
     # If the code is not correct, return error message
     if params.get('smsAddressCode') != smsAddress.code:
         return dict(isOk=0, message='Code is not valid')
     # Clear code
     smsAddress.code = None
     return dict(isOk=1)
 # If the user is removing an SMS address,
 elif 'remove' == smsAddressAction:
     db.delete(smsAddress)
     return dict(isOk=1)
def index(request):
    'Show information about people registered in the database'
    return dict(users=db.query(User).order_by(User.when_login.desc()).all())
 def test_index(self):
     'Assert that the user index page shows how many accounts are on file'
     url = self.get_url('user_index')
     # Make sure that the user index page is visible
     self.assert_('%s users' % db.query(User).count() in self.get(url).unicode_body)
def update(request):
    'Show account update page'
    userID = authenticated_userid(request)
    user = db.query(User).options(joinedload(User.sms_addresses)).get(userID)
    return dict(user=user)