def test_user_info(self): with test_data.Database.WithTestData() as data: response = self.app.get('/api/user') self.assertIsNone(json.loads(response.data)) self.assertEqual(response.status_code, 200) self.login(data.school['fragment'], data.user['email'], data.user['clearpw']) response = self.app.get('/api/user') self.assertEqual(response.status_code, 200) user = util.User(data.user, data.school) self.assertEqual( { 'name': user.name(), 'first': user['first'], 'middle': user['middle'], 'last': user['last'], 'email': user['email'], 'roles': {}, 'secret': user['secret'], 'schedules': [ user._formatSchedule(util.project_term(data.term), data.user['schedules'][0]) ], }, json.loads(response.data))
def shared_schedule(school): """Get a user by the secret on one of their schedules and return just that schedule's info.""" school = app.mongo.db.schools.find_one_or_404({'fragment': school}, {'_id': False}) c = mongo.SchoolCollections(school['fragment']) user = util.User( c.user.find_one_or_404( {'schedules.secret': flask.request.args['secret']}, { 'email': True, # For linking to instructors - do not print 'first': True, 'middle': True, 'last': True, 'schedules.$': True, }, ), school) sch = user['schedules'][0] sch['sections'] = [ sect for sect in sch['sections'] if sect['status'] != 'no' ] addInstructorCourses(c, user, term=sch['term']) schedules = user.formatSchedules() return flask.jsonify(name=user.name(), schedules=schedules)
def main(args): parser = argparse.ArgumentParser(description='Add a user.') parser.add_argument('--school', required=True) parser.add_argument('--email', required=True, action='append') parser.add_argument('--first') parser.add_argument('--middle') parser.add_argument('--last') parser.add_argument('--password') parser.add_argument('--passhash') parser.add_argument('--role', default=[], action='append') args = parser.parse_args(args) school = app.mongo.db.schools.find_one({'fragment': args.school}) if not school: sys.stderr.write('School %s does not exist.\n' % args.school) sys.exit(1) user = util.User( { 'email': args.email, 'first': args.first, 'middle': args.middle, 'last': args.last, 'schedules': [], 'secret': util.generate_secret(), 'roles': args.role, }, school) if args.password and not args.passhash: user.set_password(args.password) elif args.passhash: user['passhash'] = args.passhash else: sys.stderr.write( 'Exactly one of --passhash, --password must be provided.\n') sys.exit(1) c = mongo.SchoolCollections(school['fragment']) c.user.insert(user.user)
def reset_password(school): request = flask.request.json school = app.mongo.db.schools.find_one_or_404({'fragment': school}, {'_id': False}) c = mongo.SchoolCollections(school['fragment']) user = c.user.find_one_or_404({ 'email': request['email'], }, { '_id': True, 'email': True, 'first': True, 'middle': True, 'last': True, }) user = util.User(user, school) user['name'] = user.name() secret = util.create_verification(c, 'reset_password', user=user['_id']) msg = flask_mail.Message( "Password Reset", recipients=user['email'][0:1], ) msg.html = flask.render_template( 'email/reset_password.html', school=school, user=user.user, secret=secret, ) msg.body = flask.render_template( 'email/reset_password.txt', school=school, user=user.user, secret=secret, ) app.mail.send(msg) return flask.jsonify()
def test_login_succeed(self): with test_data.Database.WithTestData() as data: response = self.login(data.school['fragment'], data.user['email'], data.user['clearpw']) user = util.User(data.user, data.school) self.assertEqual( { 'name': user.name(), 'first': user['first'], 'middle': user['middle'], 'last': user['last'], 'email': user['email'], 'roles': {}, 'secret': user['secret'], 'schedules': [ user._formatSchedule(util.project_term(data.term), data.user['schedules'][0]) ], }, json.loads(response.data)) self.assertEqual(response.status_code, 200) self.assertEqual(flask_login.current_user['id'], data.user['id']) self.assertTrue(flask_login.current_user.is_active) self.assertTrue(flask_login.current_user.is_authenticated) self.assertFalse(flask_login.current_user.is_anonymous) self.assertIsNotNone(flask_login.current_user.get_id())
def load_user(user_id): try: school, user_id = user_id.split(':', 1) except ValueError: return None try: user_id = objectid.ObjectId(user_id) except objectid.InvalidId: return None school = app.mongo.db.schools.find_one({'fragment': school}) c = mongo.SchoolCollections(school['fragment']) user = c.user.find_one({'_id': user_id}) if user: return util.User(user, school)
def verify(school): request = flask.request.json school = app.mongo.db.schools.find_one_or_404({'fragment': school}, {'_id': False}) c = mongo.SchoolCollections(school['fragment']) verification = c.email_verification.find_one_or_404({ 'secret': request['secret'], }) type_ = verification['type'] # Check that it's still valid if verification.get('used'): return flask.jsonify(type=type_, status='used') elif (verification['expiration'].replace(tzinfo=None) < datetime.datetime.utcnow()): return flask.jsonify(type=type_, status='expired') if type_ == 'new_user': try: c.user.insert(verification['user'], w=1) except pymongo.errors.DuplicateKeyError: return flask.jsonify(type=type_, status='account_exists') elif type_ == 'reset_password': if 'password' not in request: return flask.jsonify(type=type_, status='need_password') user = util.User({}, school) user.set_password(request['password']) c.user.update({ '_id': verification['user'], }, { '$set': { 'passhash': user['passhash'] }, }, w=1) elif type_ == 'add_email': c.user.update({ '_id': verification['user'], }, {'$addToSet': { 'email': verification['email'] }}) else: raise ValueError('Unknown verification type %s' % type_) c.email_verification.update({ '_id': verification['_id'], }, { '$set': { 'used': True }, }) return flask.jsonify(type=type_, status='success')
def ical(school, secret): school = app.mongo.db.schools.find_one_or_404({'fragment': school}, {'_id': False}) c = mongo.SchoolCollections(school['fragment']) user = util.User(c.user.find_one_or_404({'secret': secret}), school) users.addInstructorCourses(c, user) schedules = user.formatSchedules(timestamps=True) cal = icalendar.Calendar() cal.add('prodid', '-//schdl/schdl//NONSGML v2.0//EN') cal.add('version', '2.0') # TODO(eitan): get timezone from school tz = pytz.timezone('America/New_York') cal.add('x-wr-calname;value=text', 'Schdl: %s' % user.name()) cal.add('x-wr-timezone', 'America/New_York') # TODO(eitan): include link to this schedule cal.add('x-wr-caldesc', 'Generated by Schdl') if schedules: cal.add_component(icalTimezone(tz, schedules)) oneday = datetime.timedelta(days=1) for sch in schedules: term = sch['term'] start = parseDate(term['start']).replace(tzinfo=tz) end = parseDate(term['end']).replace(tzinfo=tz) + oneday for sect in sch['course_sections']: if sect['user_status'] not in ('instructor', 'official'): continue for time in sect['times']: sha1 = hashlib.sha1() sha1.update( json.dumps( dict(school=school['fragment'], user=str(user['_id']), start=start, end=end, sect=sect, time=time))) uid = '%s@%s' % (sha1.hexdigest(), flask.request.host) cal.add_component(makeEvent(start, end, sect, time, uid)) filename = '%s - %s.ics' % (user.school['name'], user.name()) response = flask.Response( response=cal.to_ical(), mimetype=b'text/calendar', ) response.headers.add(b'Content-Description', b'File Transfer') response.headers.add(b'Content-Disposition', b'attachment', filename=filename.encode('utf-8')) return response
def register(school): # If logged in, just return current user if not flask_login.current_user.is_anonymous: return current_user() request = flask.request.json school = app.mongo.db.schools.find_one_or_404({'fragment': school}, {'_id': False}) c = mongo.SchoolCollections(school['fragment']) email_in_use = c.user.find_one({'email': request['email']}) if email_in_use: return flask.jsonify(), 409 # Conflict user = util.User( { 'first': request['first'], 'middle': '', 'last': request['last'], 'email': [request['email']], 'schedules': [], 'secret': util.generate_secret(), 'roles': [], }, school) user.set_password(request['password']) ttl = datetime.timedelta(days=1) secret = util.create_verification(c, 'new_user', user=user.user, ttl=ttl) user['name'] = user.name() msg = flask_mail.Message( "Confirm Your Email Address", # User must have exactly one email address at account creation recipients=user['email'], ) msg.html = flask.render_template( 'email/new_user_verify_email.html', school=school, user=user.user, secret=secret, ) msg.body = flask.render_template( 'email/new_user_verify_email.txt', school=school, user=user.user, secret=secret, ) app.mail.send(msg) return flask.jsonify()
def user_login(school): school = app.mongo.db.schools.find_one({'fragment': school}) c = mongo.SchoolCollections(school['fragment']) email = request.json['email'] password = request.json['password'] user = c.user.find_one({'email': email}) if user is None: return jsonify(reason='noaccount'), 403 if app.bcrypt.check_password_hash(user['passhash'], password): if flask_login.login_user(util.User(user, school)): c.user.update({'_id': user['_id']}, { '$set': { 'last_login': datetime.datetime.utcnow() }, }) return users.current_user() else: return jsonify(reason='unverified'), 403 else: return jsonify(reason='password'), 403
def test_PUT(self): with test_data.Database.WithTestData() as data: c = mongo.SchoolCollections(data.school['fragment']) user = c.user.find_one({'id': data.user['id']}) self.assertEqual(data.schedule['sections'][0]['status'], user['schedules'][0]['sections'][0]['status']) self.login(data.school['fragment'], data.user['email'], data.user['clearpw']) response = self.app.put( '/api/schedules/%s/%s/%s' % (data.school['fragment'], data.term['fragment'], util.encode_section_id(data.course_section['id'])), data=json.dumps(dict(status='no')), content_type='application/json') self.assertEqual(response.status_code, 200) user = util.User(c.user.find_one({'id': data.user['id']}), data.school) self.assertEqual('no', user['schedules'][0]['sections'][0]['status']) self.assertEqual( { 'name': user.name(), 'first': user['first'], 'middle': user['middle'], 'last': user['last'], 'email': user['email'], 'roles': {}, 'secret': user['secret'], 'schedules': [ user._formatSchedule(util.project_term(data.term), user['schedules'][0]) ], }, json.loads(response.data))
def test_DELETE(self): with test_data.Database.WithTestData() as data: c = mongo.SchoolCollections(data.school['fragment']) user = util.User(c.user.find_one({'id': data.user['id']}), data.school) self.assertTrue(user['schedules'][0]['sections']) self.login(data.school['fragment'], data.user['email'], data.user['clearpw']) # Delete a fake section response = self.app.delete( '/api/schedules/%s/%s/%s' % (data.school['fragment'], data.term['fragment'], util.encode_section_id('fake-section'))) self.assertEqual(response.status_code, 200) self.assertEqual( { 'name': user.name(), 'first': user['first'], 'middle': user['middle'], 'last': user['last'], 'email': user['email'], 'roles': {}, 'secret': user['secret'], 'schedules': [ user._formatSchedule(util.project_term(data.term), data.user['schedules'][0]) ], }, json.loads(response.data)) # Delete a real section response = self.app.delete( '/api/schedules/%s/%s/%s' % (data.school['fragment'], data.term['fragment'], util.encode_section_id(data.course_section['id']))) self.assertEqual(response.status_code, 200) user['schedules'][0]['sections'] = [] self.assertEqual( { 'name': user.name(), 'first': user['first'], 'middle': user['middle'], 'last': user['last'], 'email': user['email'], 'roles': {}, 'secret': user['secret'], 'schedules': [ user._formatSchedule(util.project_term(data.term), user['schedules'][0]) ], }, json.loads(response.data)) user = util.User(c.user.find_one({'id': data.user['id']}), data.school) self.assertFalse(user['schedules'][0]['sections'])