def gen_extracts(change, tmp_filepath): """ Will generate the file extracts for this change. The extracts include the thumbnail. :param change: is the change object. :param tmp_filepath: is the path to the original file """ # find previous change previous = change.entity.get_change(version=change.version - 1) extracts = [] indices = dd(lambda: 0) #each type of file will have its own count raw_extracts, parse_info = image.extract(tmp_filepath) change.file_type = parse_info.file_type raw_extracts += gen_diffs(previous, raw_extracts) for e in raw_extracts: change_extract = projects.ChangeExtract( change=change, extract_type=e.extract_type, order_index=indices[e.extract_type]) change_extract.set_contents(e.filename) Session.add(change_extract) extracts.append(change_extract) indices[e.extract_type] += 1 change.parse_type = parse_info.type change.parse_status = parse_info.status commit() return extracts
def create(**params): """ Creates a user. DO NOT EXPOSE THIS to the web api. Please. """ numusers = len(Session.query(users.User).all()) scrubbed = validate(RegisterForm, **params) logger.info(scrubbed) user = users.User() Session.add(user) user.email = scrubbed.email user.username = '******' in scrubbed and scrubbed.username or scrubbed.email user.password = scrubbed.password user.set_timezone_int(scrubbed.default_timezone) if scrubbed.get('name'): name = scrubbed.get('name').split(' ', 1) user.first_name = name[0].strip() user.last_name = len(name) == 2 and name[1].strip() or u'' else: user.first_name = scrubbed.get('first_name') user.last_name = scrubbed.get('last_name') #first user is an admin. if numusers == 0: user.role = users.ROLE_ADMIN return user
def upload_extract(real_user, user, change, **kw): """ File is binary in the request body. """ from pylons import request order_index = int(to_unicode(request.headers.get('X-Up-Order-Index'))) type = to_unicode(request.headers.get('X-Up-Type')) if not type: return None f, tmpname = tempfile.mkstemp('png') if isinstance(f, int): f = open(tmpname, 'wb') #this is inefficient. Pylons supposedly already creates a tmp file. We are instead #reading the thing into memory. I'm lazy until this beomes an issue (prolly soon) #V: please make this not suck f.write(request.environ['wsgi.input'].read()) f.close() new = False change_extract = Session.query(projects.ChangeExtract).filter_by(change=change, extract_type=type, order_index=order_index).first() if not change_extract: new = True change_extract = projects.ChangeExtract(change=change, extract_type=type, order_index=order_index) Session.add(change_extract) change_extract.set_contents(tmpname) return new and 'new' or 'existing'
def gen_extracts(change, tmp_filepath): """ Will generate the file extracts for this change. The extracts include the thumbnail. :param change: is the change object. :param tmp_filepath: is the path to the original file """ # find previous change previous = change.entity.get_change(version=change.version-1) extracts = [] indices = dd(lambda: 0) #each type of file will have its own count raw_extracts, parse_info = image.extract(tmp_filepath) change.file_type = parse_info.file_type raw_extracts += gen_diffs(previous, raw_extracts) for e in raw_extracts: change_extract = projects.ChangeExtract(change=change, extract_type=e.extract_type, order_index=indices[e.extract_type]) change_extract.set_contents(e.filename) Session.add(change_extract) extracts.append(change_extract) indices[e.extract_type] += 1 change.parse_type = parse_info.type change.parse_status = parse_info.status commit() return extracts
def create(real_user, user, organization, **params): """ Creates a project. """ class ProjectForm(formencode.Schema): name = formencode.All(fv.UnicodeString(not_empty=True), UniqueName(organization)) description = fv.UnicodeString(not_empty=False) scrubbed = validate(ProjectForm, **params) project = projects.Project(name=scrubbed.name, creator=user, description=scrubbed.description, organization=organization) Session.add(project) Session.flush() #email users = organization.interested_users if user in users: users.remove(user) email.send(users, 'create_project.txt', { 'project': project, 'creator': user }) return project
def edit(real_user, user, file, **kwargs): """ Editing of the campaigns. Supports editing one param at a time. Uses the FieldEditor paradigm. """ editor = Editor(file) editor.edit(real_user, user, file, **kwargs) Session.flush() Session.refresh(file) return file
def edit(real_user, user, change, **kwargs): """ Editing of the campaigns. Supports editing one param at a time. Uses the FieldEditor paradigm. """ editor = Editor() editor.edit(real_user, user, change, **kwargs) Session.flush() Session.refresh(change) return change
def create_project(user=None, organization=None, role=users.APP_ROLE_ADMIN, **kw): kw.setdefault("name", create_unique_str(u'project')) kw.setdefault("description", create_unique_str(u"description")) kw.setdefault("status", STATUS_APPROVED) org = organization or create_organization(user, role) project = projects.Project(organization=org, creator=user or org.creator, **kw) Session.add(project) Session.flush() return project
def create_user(is_admin=False, **kw): kw.setdefault("email", create_email_address()) kw.setdefault("username", create_unique_str(u'user', extra=u'')) kw.setdefault("password", u'testpassword') if is_admin: kw.setdefault("role", users.ROLE_ADMIN) else: kw.setdefault("role", users.ROLE_USER) user = users.User(**kw) Session.add(user) Session.flush() return user
def project_user_module(real_user, user, organization, project=None): """ """ # email: user def list org_users = api.organization.get_users(real_user, user, organization, status=STATUS_APPROVED) user_map = dict([(ou.user.email, v1.user.get().output(ou.user)) for ou in org_users]) #email list for lookup emails = [u.user.email for u in org_users] # list of users in the project and their role invite_url = None proj_users = [] if project: invites = Session.query(users.Invite).filter_by(type=users.INVITE_TYPE_PROJECT, object_id=project.id, status=STATUS_PENDING) invites = [{'invite': True, 'user': {'name': i.invited_email}} for i in invites] proj_users = api.project.get_users(real_user, user, project, status=STATUS_APPROVED) proj_users = v1.project.get_users().output(proj_users) + invites invite_url = h.api_url('invite', 'create', project=c.project.eid) params = { 'emails': emails, 'projectUsers': proj_users, 'userMap': user_map, 'roleUrl': h.api_url('project', 'set_user_role', project=project and project.eid or None), 'syncUrl': h.api_url('project', 'attach_users'), 'inviteUrl': invite_url } return params
def create_organization(user=None, role=users.APP_ROLE_ADMIN, status=STATUS_APPROVED, **kw): """ create an org and will attach a user to it. If no user specified, will make one. """ kw.setdefault("name", create_unique_str(u'org')) kw.setdefault("url", u'http://%s.com' % create_unique_str(u'url')) kw.setdefault("subdomain", create_str(length=10)) user = user or create_user() org = users.Organization(creator=user, **kw) Session.add(org) #connect user to org as admin of org org_user = org.attach_user(user, role=role, status=status) Session.flush() return org
def get(u=None, username=None): if not u and not username: abort(403) elif u: return u u = Session.query(users.User).filter_by(username=username).first() if not u: abort(403) return u
def _to_python(self, value, state): # we don't support multiple values, so we run a quick check here (we got a webapp where this was a problem) if type(value) != type(u""): raise fv.Invalid('You must supply a valid email.', value, state) user = Session.query(users.User).filter(sa.func.lower(users.User.email)==value.lower()).first() # if this user is the same as the logged in one then don't throw the error - allows keeping old email address when editing contact info if user and user != self.user: raise fv.Invalid('That user already exists. Please choose another.', value, state) return value
def _to_python(self, value, state): if type(value) != type(u""): raise fv.Invalid('You must supply a valid subdomain.', value, state) subd = Session.query(users.Organization).filter(sa.func.lower(users.Organization.subdomain)==value.lower()).first() if subd: raise fv.Invalid('%s is already taken' % value, value, state) return value.lower()
def get(real_user, user, organization, project=None): if not user and not project: abort(403) if project: q = Session.query(projects.Project).filter_by(organization=organization) p = q.filter(sa.or_(projects.Project.eid==project, projects.Project.slug==project)).first() CanReadProject().check(real_user, user, project=p) return p #this will do the check to make sure to return only projects the user can see return organization.get_projects(user)
def get(real_user=None, user=None, organization=None, subdomain=None): """ get a single project, or all the user's projects """ if organization: CanReadOrg().check(real_user, user, organization=organization) return organization elif subdomain: return Session.query(users.Organization).filter_by(subdomain=subdomain).first() return user.get_organizations()
def get(real_user=None, user=None, organization=None, subdomain=None): """ get a single project, or all the user's projects """ if organization: CanReadOrg().check(real_user, user, organization=organization) return organization elif subdomain: return Session.query( users.Organization).filter_by(subdomain=subdomain).first() return user.get_organizations()
def _to_python(self, value, state): # we don't support multiple values, so we run a quick check here (we got a webapp where this was a problem) if type(value) != type(u""): raise fv.Invalid('You must supply a valid email.', value, state) user = Session.query(users.User).filter( sa.func.lower(users.User.email) == value.lower()).first() # if this user is the same as the logged in one then don't throw the error - allows keeping old email address when editing contact info if user and user != self.user: raise fv.Invalid( 'That user already exists. Please choose another.', value, state) return value
def _to_python(self, value, state): if type(value) != type(u""): raise fv.Invalid('You must supply a valid subdomain.', value, state) subd = Session.query(users.Organization).filter( sa.func.lower(users.Organization.subdomain) == value.lower()).first() if subd: raise fv.Invalid('%s is already taken' % value, value, state) return value.lower()
def _to_python(self, value, state): # we don't support multiple values, so we run a quick check here (we got a webapp where this was a problem) if not isinstance(value, unicode): raise fv.Invalid('You must supply a valid string.', value, state) file = Session.query(projects.Entity ).filter(projects.Entity.project==self.project ).filter(projects.Entity.path==self.file.path ).filter(projects.Entity.name==value ) file = file.filter(projects.Entity.id != self.file.id) file = file.first() if file: raise fv.Invalid('A file with this name already exists. Please choose another.', value, state) return value
def _to_python(self, value, state): # we don't support multiple values, so we run a quick check here (we got a webapp where this was a problem) if not isinstance(value, unicode): raise fv.Invalid('You must supply a valid string.', value, state) project = Session.query(projects.Project ).filter(projects.Project.organization==self.organization ).filter(projects.Project.name==value ) if self.project: project = project.filter(projects.Project.id != self.project.id) project = project.first() if project: raise fv.Invalid('A project with this name already exists. Please choose another.', value, state) return value
def get(real_user, user, change=None, parse_type=None, parse_status=None): if change: return change if not parse_status and not parse_type: raise ClientException('either parse_type or parse_status') q = Session.query(projects.Change) if parse_type: q = q.filter(projects.Change.parse_type.in_(parse_type)) if parse_status: q = q.filter(projects.Change.parse_status.in_(parse_status)) q = q.order_by(sa.asc(projects.Change.created_date)) return q.all()
def _to_python(self, value, state): # we don't support multiple values, so we run a quick check here (we got a webapp where this was a problem) if not isinstance(value, unicode): raise fv.Invalid('You must supply a valid string.', value, state) file = Session.query(projects.Entity).filter( projects.Entity.project == self.project).filter( projects.Entity.path == self.file.path).filter( projects.Entity.name == value) file = file.filter(projects.Entity.id != self.file.id) file = file.first() if file: raise fv.Invalid( 'A file with this name already exists. Please choose another.', value, state) return value
def create(real_user, user, **params): """ Creates an organization. Attaches it to User. """ scrubbed = validate(CreateForm, **params) scrubbed.setdefault('is_active', True) scrubbed['name'] = scrubbed['company_name'] del scrubbed['company_name'] #attach the user as a creator. org = users.Organization(creator=user, **scrubbed) Session.add(org) #connect user to org as admin of org org.attach_user(user, role=users.APP_ROLE_ADMIN, status=STATUS_APPROVED) Session.add(activity.NewOrganization(user, org)) Session.flush() return org
indices[e.extract_type] += 1 change.parse_type = parse_info.type change.parse_status = parse_info.status commit() return extracts if __name__ == '__main__': """ accept 2 params: eid of the change path to file, the raw file (PSD, PDF, whatev...) """ if len(sys.argv) != 4: print 'Usage: %s development.ini change_eid /path/to/tmp_file.blah' % sys.argv[ 0] exit(1) ini, eid, filepath = sys.argv[1:] load_config(os.path.abspath(ini)) change = Session.query(projects.Change).filter_by(eid=eid).first() if not change: print 'Cannot find change %s' % eid exit(1) print gen_extracts(change, filepath)
def test_all(self): a = fh.create_user(is_admin=True) org_owner = fh.create_user() inviteme = fh.create_user(email="*****@*****.**") org = fh.create_organization(user=org_owner, subdomain='cats', name='CATS!') project = fh.create_project(user=org_owner, organization=org, name=u"helloooo") change = project.add_change(org_owner, u"/main/project/arnold/foobar.gif", file_path('ffcc00.gif'), u"this is a new change") file = change.entity self.flush() email = '*****@*****.**' #invite user not in the system to file invite = api.invite.create(a, org_owner, email, entity=file) #based on email only assert self.throws_exception(lambda: api.invite.create(a, org_owner, email, entity=file)).code == err.DUPLICATE assert invite.role == APP_ROLE_READ assert invite.invited_user == None assert invite.user == org_owner assert invite.invited_email == email assert invite.object == file emails = self.get_sent_mails() assert len(emails) == 1 assert api.invite.get(invite.eid) == invite #need to create a new user to accept u = fh.create_user(email=email, username=email) self.flush() api.invite.accept(u, u, invite.eid) self.flush() Session.refresh(invite) assert invite.invited_user == u assert invite.status == STATUS_APPROVED #invite user in system to proj invite = api.invite.create(org_owner, org_owner, email, project=project) assert invite.invited_user == u assert invite.invited_email == email assert invite.object == project emails = self.get_sent_mails() assert len(emails) == 2 api.invite.reject(u, u, invite.eid) self.flush() Session.refresh(invite) assert invite.invited_user == u assert invite.status == STATUS_REJECTED #invite user in system to org invite = api.invite.create(org_owner, org_owner, email, organization=org, role=APP_ROLE_WRITE) assert invite.object == org emails = self.get_sent_mails() assert len(emails) == 3 # FAIL #already invited assert self.throws_exception(lambda: api.invite.create(a, org_owner, email, entity=file)).code == err.DUPLICATE #no params ex = self.throws_exception(lambda: api.invite.create(org_owner, org_owner, email)).code assert ex# == err.NOT_FOUND #not in org at all assert self.throws_exception(lambda: api.invite.create(inviteme, inviteme, email, organization=org)).code == err.FORBIDDEN #in org as writer assert self.throws_exception(lambda: api.invite.create(u, u, email, entity=file)).code == err.FORBIDDEN #validation assert 'email' in self.throws_exception(lambda: api.invite.create(org_owner, org_owner, '', entity=file)).error_dict
Session.add(change_extract) extracts.append(change_extract) indices[e.extract_type] += 1 change.parse_type = parse_info.type change.parse_status = parse_info.status commit() return extracts if __name__ == '__main__': """ accept 2 params: eid of the change path to file, the raw file (PSD, PDF, whatev...) """ if len(sys.argv) != 4: print 'Usage: %s development.ini change_eid /path/to/tmp_file.blah' % sys.argv[0] exit(1) ini, eid, filepath = sys.argv[1:] load_config(os.path.abspath(ini)) change = Session.query(projects.Change).filter_by(eid=eid).first() if not change: print 'Cannot find change %s' % eid exit(1) print gen_extracts(change, filepath)
class FieldEditor(object): """ The edit functions for a given object are big and tend to be error prone. This class allows you to just specify a validator class, the params you want to edit, and some functions to edit those params. This class will handle editing of one variable at a time, it will catch and package up multiple errors, and it will do general authorization. You just extend it and add your edit functions with name edit_<param_name> Then you instantiate and call edit(). Example function: def edit_budget(real_user, user, campaign, key, value): raise exceptions.ClientException('OMG bad shit is happening!', field=key) 'key' would be 'budget' Notes: * If the user is not an admin and he tries ot edit an admin field, the editor will just ignore the field as if he had not specified it. * Your editing can work one param at a time. so /api/v1/campaign/edit?name=my+name /api/v1/campaign/edit?key=name&value=my+name are equivalent * Your field editing functions can be passed None so /api/v1/campaign/edit?cpc= would unset the CPC. If you dont want to accept None, check for it in your edit_ function, not in the validator. * You must do object ownership authorization outside of this editor. The only auth this thing does is an admin check for the editing of admin fields. Use the @auth(must_own='asd') on your edit api function. * Your edit_ functions can raise ClientExceptions. They will be packaged up in a CompoundException and be returned to the client side as a collection. If you raise an AdrollException, it will get through to the error middleware. """ def __init__(self, fields, admin_fields, validator): self.validator = validator self.fields = fields self.admin_fields = admin_fields def _edit_generic(self, name, obj, key, param, can_be_none=False): if not can_be_none and param == None: raise exceptions.ClientException('Please enter a %s' % name, field=key) old = getattr(obj, key) setattr(obj, key, param) self.log(name, key, old, getattr(obj, key)) def log(self, field, key, old_val, new_val): logger.info( '%s edited by %s: %s (%s) = %s from %s' % (self.object, self.real_user, field, key, new_val, old_val)) def edit(self, real_user, user, obj, key=None, value=None, **kwargs): self.real_user = real_user self.user = user self.object = obj self.params = kwargs # for the single field edit if key and value != None and key not in kwargs: kwargs[key] = value # There is no authorization check in here. This is effectively it. # If the user is not an admin, the admin fields are stripped out. editable_keys = set(real_user.is_admin() and (self.fields + self.admin_fields) or self.fields) # is there anything we can edit? to_edit = [k for k in kwargs.keys() if k in editable_keys] if not to_edit: raise ClientException('Specify some parameters to edit, please.', code=INCOMPLETE) # we fill out the kwargs so we dont piss off the validator. hack. poo. Must have all # fields as the validator will too. for k in self.fields + self.admin_fields: if k not in kwargs or k not in editable_keys: kwargs[k] = None params = validate(self.validator, **kwargs) #this is for collecting errors. error = CompoundException('Editing issues!', code=FAIL) # only go through the keys that we got in the original call/request (to_edit) for k in to_edit: if k not in editable_keys: continue param = params[k] fn_name = 'edit_%s' % k if hasattr(self, fn_name): try: results = getattr(self, fn_name)(real_user, user, obj, k, param) except ClientException, e: # if error from editing, we will package it up so as to # return all errors at once error.add(e) else: #this is an adroll exception cause it should bubble up to a WebApp email raise AppException('Cannot find %s edit function! :(' % fn_name, code=INCOMPLETE) if error.has_exceptions: raise error Session.flush() return True
def test_all(self): a = fh.create_user(is_admin=True) org_owner = fh.create_user() inviteme = fh.create_user(email="*****@*****.**") org = fh.create_organization(user=org_owner, subdomain='cats', name='CATS!') project = fh.create_project(user=org_owner, organization=org, name=u"helloooo") change = project.add_change(org_owner, u"/main/project/arnold/foobar.gif", file_path('ffcc00.gif'), u"this is a new change") file = change.entity self.flush() email = '*****@*****.**' #invite user not in the system to file invite = api.invite.create(a, org_owner, email, entity=file) #based on email only assert self.throws_exception(lambda: api.invite.create( a, org_owner, email, entity=file)).code == err.DUPLICATE assert invite.role == APP_ROLE_READ assert invite.invited_user == None assert invite.user == org_owner assert invite.invited_email == email assert invite.object == file emails = self.get_sent_mails() assert len(emails) == 1 assert api.invite.get(invite.eid) == invite #need to create a new user to accept u = fh.create_user(email=email, username=email) self.flush() api.invite.accept(u, u, invite.eid) self.flush() Session.refresh(invite) assert invite.invited_user == u assert invite.status == STATUS_APPROVED #invite user in system to proj invite = api.invite.create(org_owner, org_owner, email, project=project) assert invite.invited_user == u assert invite.invited_email == email assert invite.object == project emails = self.get_sent_mails() assert len(emails) == 2 api.invite.reject(u, u, invite.eid) self.flush() Session.refresh(invite) assert invite.invited_user == u assert invite.status == STATUS_REJECTED #invite user in system to org invite = api.invite.create(org_owner, org_owner, email, organization=org, role=APP_ROLE_WRITE) assert invite.object == org emails = self.get_sent_mails() assert len(emails) == 3 # FAIL #already invited assert self.throws_exception(lambda: api.invite.create( a, org_owner, email, entity=file)).code == err.DUPLICATE #no params ex = self.throws_exception( lambda: api.invite.create(org_owner, org_owner, email)).code assert ex # == err.NOT_FOUND #not in org at all assert self.throws_exception(lambda: api.invite.create( inviteme, inviteme, email, organization=org)).code == err.FORBIDDEN #in org as writer assert self.throws_exception(lambda: api.invite.create( u, u, email, entity=file)).code == err.FORBIDDEN #validation assert 'email' in self.throws_exception(lambda: api.invite.create( org_owner, org_owner, '', entity=file)).error_dict