class Card(models.Model): customer = BigForeignKey(Customer, related_name='cards', on_delete=models.CASCADE, null=True) number = EncryptedCharField(max_length=255) last4_number = models.IntegerField(blank=True) expiration_year = EncryptedIntegerField(max_length=255) expiration_month = EncryptedIntegerField(max_length=255) cvv = EncryptedCharField(max_length=255) created = models.DateTimeField("Created at", auto_now_add=True) updated = models.DateTimeField(verbose_name="Updated at", auto_now=True) class Meta: ordering = ('number', ) def __unicode__(self): return self.masked_number @property def masked_number(self): return u'XXXX%s' % self.last4_number def set_last4_number(self): self.last4_number = self.number[-4:] return self.last4_number
class SMTPAccount(Account): """ An account for sending emails via SMTP. """ smtp_host = EncryptedCharField(max_length=100, null=True) smtp_port = models.IntegerField(null=True) smtp_username = EncryptedCharField(max_length=100, null=True) smtp_password = EncryptedCharField(max_length=100, null=True) def send_email(self, user, contact, subject, html, text): msg = MIMEMultipart("alternative") msg['Subject'] = subject msg['From'] = self.email_address msg['To'] = contact.email part1 = MIMEText(text, "plain") part2 = MIMEText(html, "html") msg.attach(part1) msg.attach(part2) s = smtplib.SMTP("%s:%s" % (self.smtp_host, self.smtp_port)) if self.smtp_username: s.login(self.smtp_username, self.smtp_password) s.sendmail(self.email_address, contact.email, msg.as_string()) s.quit()
class TextCaptchaToken(CaptchaToken): """docstring for TextCaptcha.""" result = EncryptedCharField(max_length=256) def create(self, file_name, file_data, resolved, result='', unsolvable=False): super(TextCaptchaToken, self).create(file_name, file_data, resolved) self.result = result self.captcha_type = "text" return self # solves token if 3 matching proposals were made # marks token as unsolveable if more than 6 resolutions didn't result in # solved token def try_solve(self): # checks if there is a solution for a token based on saved proposals proposals = self.proposals most_common = proposals.most_common() num_proposoals = sum(proposals.values()) if len(proposals.values()) >= 6: # more than six different proposals self.unsolvable = True self.resolved = True self.save() elif num_proposoals >= 3: if most_common[0][1] >= 3: self.resolved = True self.result = most_common[0][0] self.save()
class Contract(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) #user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) wage = EncryptedCharField(max_length=200) start = models.DateField(auto_now=False, auto_now_add=False) end = models.DateField(auto_now=False, auto_now_add=False) work_permit_no = EncryptedCharField(max_length=200) visa_no = EncryptedCharField(max_length=200) visa_expire = EncryptedDateTimeField() created_at = models.DateTimeField(default=timezone.now) updated_at = models.DateTimeField(blank=True, null=True) def update(self): self.updated_at = timezone.now() self.save() def __unicode__(self): return self.user.username + " / " + self.wage + " / " + self.work_permit_no + " / " + self.visa_no + " / " + self.visa_expire.strftime( '%Y-%m-%d')
class DataConnection(AuditableTable): """How to connect to a datasource. This uses sqlalchemy behind the scenes. See http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html for details""" class Meta: verbose_name_plural = "Data Connections" drivername = models.CharField( max_length=100, help_text="The name of the database backend. This name will correspond " "to a module in sqlalchemy/databases or a third party plug-in. Examples: mysql, sqlite" ) dialect = models.CharField( max_length=100, blank=True, help_text="Optionally used for certain drivers. For example, use " "'oursql' for OurSQL") username = models.CharField(max_length=300, blank=True) password = EncryptedCharField( passphrase_setting='SECRET_KEY', max_length=300, help_text= "WARNING: Assume this is not safe! It is encrypted using unvetted code I " "found on the internet. It will be encrypted when stored in " "the database BUT the key is stored on your sever. " " It will also be available to any users using the admin interface and " "is sent to the DB server and to admin users in clear text.", blank=True) host = models.CharField(max_length=300, help_text="The name of the host", blank=True) port = models.IntegerField(help_text="The port number", null=True, blank=True) database = models.CharField(max_length=300, help_text="The database name") def get_db_connection(self): url = sqlalchemy.engine.url.URL(drivername=self.drivername, username=self.username or None, password=self.password or None, host=self.host or None, port=self.port or None, database=self.database) #Sqlalchemy doesn't seem to let us specify dialect in URL, I guess we have to hack it in?? s_url = str(url) if self.dialect: drivername, the_rest = str(url).split('://') s_url = drivername + '+' + self.dialect + '://' + the_rest engine = sqlalchemy.create_engine(s_url) return engine.connect() def __unicode__(self): return "%s@%s/%s (%s)" % (self.username, self.host, self.database, self.drivername)
class RippleWalletCredentials(SingletonModel): address = models.CharField( max_length=35, validators=[ripple_address_validator], verbose_name='Address', ) secret = EncryptedCharField(max_length=29, verbose_name='Secret key') def __str__(self): return 'Ripple Wallet Credentials' class Meta: verbose_name = 'Ripple Wallet Credentials'
class UserCredential(PlCoreBase): user = models.ForeignKey( User, related_name='usercredentials', help_text="The User this credential is associated with") name = models.SlugField(help_text="The credential type, e.g. ec2", max_length=128) key_id = models.CharField(help_text="The backend id of this credential", max_length=1024) enc_value = EncryptedCharField( help_text="The key value of this credential", max_length=1024) def __unicode__(self): return self.name
class SliceCredential(PlCoreBase): slice = models.ForeignKey( Slice, related_name='slicecredentials', help_text="The User this credential is associated with") name = models.SlugField(help_text="The credential type, e.g. ec2", max_length=128) key_id = StrippedCharField(help_text="The backend id of this credential", max_length=1024) enc_value = EncryptedCharField( help_text="The key value of this credential", max_length=1024) xos_links = [ModelLink(Slice, via='slice')] def __unicode__(self): return self.name
class ControllerCredential(PlCoreBase): objects = ControllerLinkManager() deleted_objects = ControllerLinkDeletionManager() controller = models.ForeignKey( Controller, related_name='controllercredentials', help_text="The User this credential is associated with") name = models.SlugField(help_text="The credential type, e.g. ec2", max_length=128) key_id = models.CharField(help_text="The backend id of this credential", max_length=1024) enc_value = EncryptedCharField( help_text="The key value of this credential", max_length=1024) def __unicode__(self): return self.name
class Dot(models.Model): note = models.ForeignKey(Note, related_name='dots') diagram = models.ForeignKey(Diagram, related_name='diagrams') marker = models.ForeignKey(Marker, related_name='markers') position_x = models.PositiveSmallIntegerField() position_y = models.PositiveSmallIntegerField() text = EncryptedCharField(default='', max_length=1024) def __unicode__(self): return ('Note - %s / Diagram - %s / Marker - %s - %s' % (str(self.note), self.diagram, self.marker, self.text)) def save(self, *args, **kwargs): if self.note.is_finalized: raise NoteIsAlreadyFinalizedError() super(Dot, self).save(*args, **kwargs) def delete(self, *args, **kwargs): if self.note.is_finalized: raise NoteIsAlreadyFinalizedError() super(Dot, self).delete(*args, **kwargs)
class Db(models.Model): name_short = models.CharField(unique=True, max_length=10) name_long = models.CharField(unique=True, max_length=128) type = models.CharField(max_length=10, choices=(('MySQL', 'MySQL'), ('Postgres', 'Postgres'), ('Hive2', 'Hive2')), default='None') host = models.CharField(max_length=1024) db = models.CharField(max_length=1024) port = models.IntegerField() username = models.CharField(max_length=128) password_encrypted = EncryptedCharField(max_length=1024, ) # TODO FIX SPELLING MISTAKE create_time = models.DateTimeField(auto_now_add=True, editable=False) modified_time = models.DateTimeField(auto_now=True, editable=False) tags = TaggableManager(blank=True) def __str__(self): return self.name_short def clean(self): return True # TODO Validate database connection
class Note(models.Model): CREATED_DATE_FORMAT = '%m/%d/%Y' healer = models.ForeignKey('healers.Healer', related_name='notes') client = models.ForeignKey('clients.Client') created_date = models.DateTimeField(auto_now_add=True) finalized_date = models.DateTimeField(null=True, blank=True) subjective = EncryptedCharField(default='', max_length=1024) objective = EncryptedCharField(default='', max_length=1024) assessment = EncryptedCharField(default='', max_length=1024) plan = EncryptedCharField(default='', max_length=1024) condition = models.PositiveSmallIntegerField(null=True, blank=True) # 0 - 10 class Meta: ordering = ['-pk'] def __unicode__(self): return 'Healer: %s / Client: %s / Created: %s' % ( self.healer, self.client, self.created_date) @property def is_finalized(self): return self.finalized_date is not None @property def created_date_formated(self): def get_format(): format = '%m/%d' if datetime.now() - self.created_date >= timedelta(days=365): format += '/%y' return format return self.created_date.strftime(get_format()) def as_dict(self): return { 'client_name': str(self.client), 'created_date': self.created_date_formated, 'created_date_full': self.created_date.strftime(Note.CREATED_DATE_FORMAT), 'subjective': self.subjective, 'objective': self.objective, 'assessment': self.assessment, 'condition': self.condition, 'plan': self.plan, 'is_finalized': self.is_finalized, } def get_note_prev_next_ids(self): def get_note_id(type): if type == 'prev': notes_filtered = notes.filter(pk__lt=self.pk) elif type == 'next': notes_filtered = notes.filter(pk__gt=self.pk).order_by('pk') if notes_filtered.exists(): return notes_filtered[0].pk notes = Note.objects.filter(healer=self.healer, client=self.client) notes_prev = get_note_id('prev') notes_next = get_note_id('next') return notes_prev, notes_next def get_dots(self): return { dot.pk: { 'diagram_id': dot.diagram.pk, 'marker_id': dot.marker.pk, 'position': [dot.position_x, dot.position_y], 'text': dot.text, } for dot in self.dots.all() } def duplicate(self): def duplicate_note(): note = self note.pk = None note.created_date = None note.finalized_date = None note.save() return note def duplicate_dots(): for dot in original_dots: dot.pk = None dot.note = note dot.save() original_dots = self.dots.all() note = duplicate_note() duplicate_dots() return note.pk def finalize(self): if self.is_finalized: raise NoteIsAlreadyFinalizedError() else: self.finalized_date = datetime.now() self.save(True) def save(self, force=False, *args, **kwargs): if (self.pk is None and not self.healer.user.has_perm( healers.models.Healer.NOTES_PERMISSION) and (Note.objects.filter(healer=self.healer).count() >= settings.NUMBER_OF_FREE_NOTES)): raise NoNotesPermissionError() if not force and self.is_finalized: raise NoteIsAlreadyFinalizedError() super(Note, self).save(*args, **kwargs) def delete(self, *args, **kwargs): if self.is_finalized: raise NoteIsAlreadyFinalizedError() super(Note, self).delete(*args, **kwargs)
class Integration(Entity): DEFAULT_INTEGRATION_NAME = 'Default' ACCESS_KEY_LENGTH = 32 SECRET_KEY_LENGTH = 48 tenant = models.ForeignKey(Tenant, related_name='integrations', on_delete=models.CASCADE) policy = models.OneToOneField(Policy, related_name='integration', on_delete=models.CASCADE) access_key = models.CharField(max_length=128, unique=True) secret_key = EncryptedCharField(max_length=128, unique=True) endpoint = models.URLField(blank=True) notes = models.TextField(blank=True, null=True) uid = models.CharField(max_length=16, blank=True, null=True) def save(self, *args, **kwargs): if not self.uid: self.uid = uuid.uuid4().get_hex()[:8] super(Integration, self).save(*args, **kwargs) @staticmethod def create(tenant, name, notes): return Integration.objects.create( tenant=tenant, name=name, notes=notes, policy=Policy.objects.create(name=name), access_key=get_random_string(Integration.ACCESS_KEY_LENGTH), secret_key=get_random_string(Integration.SECRET_KEY_LENGTH), ) def generate_auth_session_token(self, username, expires_at): return '' def generate_auth_session_portal_url(self, kind, username, expires_at): return self.endpoint + '/' + kind + '/?token=' + self.generate_auth_session_token( username, expires_at) def enroll(self, username): client = Client.objects.filter(integration=self, username=username).first() if client: return None, errors.MFAError('username `{0}` already exists', username) Enrollment = apps.get_model('enrollment', 'Enrollment') expiration_mins = self.policy.get_configuration( Configuration.KIND_ENROLLMENT_EXPIRATION_IN_MINUTES ) or Enrollment.DEFAULT_EXPIRATION_IN_MINUTES expires_at = timezone.now() + timedelta(minutes=expiration_mins) entity = Enrollment.objects.create( integration=self, policy=self.policy, username=username, binding_context=None, expires_at=expires_at, portal_url=self.generate_auth_session_portal_url( 'enrollment', username, expires_at)) return entity, None def challenge(self, client, data): assert client.integration == self Challenge = apps.get_model('challenge', 'Challenge') device = None if data['device_pk']: device = client.devices.filter(pk=data['device_pk']).first() if not device: return None, errors.MFAInconsistentStateError( 'device `{0}` does not exist for client `{1}`'.format( data['device_pk'], client.username)) # obtain the challenge expiration in minute expiration_mins = self.policy.get_configuration( Configuration.KIND_CHALLENGE_EXPIRATION_IN_MINUTES ) or Challenge.DEFAULT_EXPIRATION_IN_MINUTES expires_at = timezone.now() + timedelta(minutes=expiration_mins) # create the challenge entity entity = Challenge.objects.create( client=client, device=device, policy=self.policy, reference=data['reference'] if 'reference' in data else '', expires_at=expires_at, portal_url=self.generate_auth_session_portal_url( 'challenge', client.username, expires_at)) return entity, None
class Remote(TimeStampedModel): name = models.CharField(max_length=100) key = models.SlugField(max_length=120, unique=True) developer = models.ForeignKey('users.User', related_name="developer") endpoint = models.URLField(null=True, blank=True) configuration = models.TextField(default=default_config_json()) secret = EncryptedCharField(max_length=36) users = models.ManyToManyField('users.User') allow_all_origins = models.BooleanField( default=False, verbose_name="Development mode enabled") def delete(self): self.delete_cache() return super(Remote, self).delete() def save(self, *args, **kwargs): if not self.pk: self.secret = uuid.uuid4() # Sync allow all origins with redis. conn = get_redis_connection('default') conn.set(self.get_allow_all_key(), int(self.allow_all_origins)) self.sync_values() # Save the object - we're done. return super(Remote, self).save(*args, **kwargs) def sync_values(self): # Makes sure the values are properly synced up on cache. configuration_json = json.loads(self.configuration) fields = configuration_json["fields"] # Get cached values and types values = self.get_values() types = self.get_types() for key, field in fields.iteritems(): # If the field is not an action if field["type"] != "action": # Get the default value and the type as per the JSON conf. default_value = field["default"] field_type = field["type"] # Get the type we have cached, in case the types differ. cached_type = types.get(key) if cached_type != field_type: # If they do differ, set the value to the default value and update the type values[key] = default_value types[key] = field_type else: # If they don't, just add the value if the key was not populated. cached_value = values.get(key, default_value) values[key] = cached_value removed_keys = set(values.keys()) - set(fields.keys()) for key in removed_keys: values.pop(key) self.save_values(values) self.save_types(types) def emit_socket_event(self, event_type, action_name=None): socket_emit_url = 'http://localhost:9000/private/io/emit/' emit_password = os.environ['SUPREMOTE_SOCKET_KEY'] request_body = { 'event_type': event_type, 'room': self.key, 'message': self.get_values(), 'password': emit_password } if action_name: request_body.update({'action_name': action_name}) try: grequests.post(socket_emit_url, data=json.dumps(request_body), headers={ 'Content-Type': 'application/json' }).send() except Exception: pass def update_endpoint(self, user_email): self.emit_socket_event('update') if self.endpoint: values = self.get_values() request_body = { 'user': user_email, 'remote_key': self.key, 'data': values, 'updated': str(timezone.now()), } transaction_id = uuid.uuid4() signature = get_body_signature(request_body, self.secret, transaction_id) r = grequests.post(self.endpoint, data=json.dumps(request_body), headers=get_headers(signature, transaction_id)).send() def trigger_action(self, action_name, user_email): configuration = self.get_configuration() action_dictionary = configuration['fields'].get(action_name) if action_dictionary: endpoint = action_dictionary.get( 'endpoint') or self.endpoint or None throttle = action_dictionary['throttle'] throttle_threshold = timezone.now() - datetime.timedelta( seconds=throttle) # Look for latest throttle entry for this action. throttle_entry_count = ActionThrottle.objects.filter( remote=self, key=action_name, created__gt=throttle_threshold, status_code=200, ).count() if throttle_entry_count == 0: self.emit_socket_event('action', action_name=action_name) if endpoint: # Action must be triggered. request_body = { 'user': user_email, 'remote_key': self.key, 'data': { 'action_id': action_name, 'triggered': str(timezone.now()), } } transaction_id = uuid.uuid4() signature = get_body_signature(request_body, self.secret, transaction_id) headers = get_headers(signature, transaction_id) grequests.post(endpoint, data=json.dumps(request_body), headers=headers).send() # FIXME: Put actual response status code. ActionThrottle(remote=self, key=action_name, status_code=200).save() return True else: # Action is throttled and must not be carried on. return False else: raise KeyError("%s is not an action defined by this remote." % action_name) def get_configuration(self): return json.loads(self.configuration) def delete_cache(self): conn = get_redis_connection('default') cache = get_cache('default') cache.delete_pattern(self.get_cache_key()) cache.delete_pattern(self.get_type_cache_key()) cache.delete_pattern(self.get_domains_key()) conn.delete(self.get_allow_all_key()) def get_types(self): cache = get_cache('default') key = self.get_type_cache_key() return cache.get(key, {}) def save_types(self, types): cache = get_cache('default') key = self.get_type_cache_key() cache.set(key, types, None) def get_values(self): conn = get_redis_connection('default') key = self.get_cache_key() return json.loads(conn.get(key) or '{}') def save_values(self, values): conn = get_redis_connection('default') key = self.get_cache_key() conn.set(key, json.dumps(values)) def save_default(self, key, field): # Saves the default value for this key if the key does not have an associated value. values = self.get_values() default_value = field["default"] cached_value = values.get(key, default_value) values[key] = cached_value self.save_values(values) def get_cache_key(self): return '{}.values'.format(self.key) def get_type_cache_key(self): return '{}.types'.format(self.key) def get_domains_key(self): return '{}.domains'.format(self.key) def get_allow_all_key(self): return '{}.allow_all_origins'.format(self.key) def get_absolute_url(self): return reverse('core:remote_detail', kwargs={ 'remote_id': self.pk, 'remote_key': self.key, }) def clean(self): #Load the JSON schema schema_file = open(path_to_schema()) schema = self.validate_schema(schema_file) configuration_json = self.validate_configuration( schema, self.configuration) schema_file.close() return super(Remote, self).clean() def validate_schema(self, schema_file): try: schema = json.load(schema_file) except: raise ValidationError("Schema appears to be invalid.") return schema def validate_configuration(self, schema, configuration): validator = Draft4Validator(schema) try: configuration_json = json.loads(self.configuration) except: raise ValidationError( "The given configuration file is not valid JSON.") error = best_match(validator.iter_errors(configuration_json)) if error: raise ValidationError(error.message) for key, field in configuration_json["fields"].iteritems(): self.validate_field(key, field) index = 0 fieldset_entries = [] for fieldset in configuration_json["fieldsets"]: for field in fieldset["fields"]: if field not in fieldset_entries: fieldset_entries.append(field) else: raise ValidationError( "Fieldset at index {} repeats field key '{}'.".format( index, field)) if field not in configuration_json["fields"]: raise ValidationError( "The key '{}'' is not defined as a field in fieldset at index {}" .format(field, index)) index += 1 return configuration_json def validate_field(self, key, field): acceptable_action_classes = [ 'danger', 'primary', 'default', 'warning', 'success', 'info' ] if field["type"] == "text": if field["maxLength"] <= 0: raise ValidationError( "{}: maxLength must be greater than 0.".format(key)) if len(field["default"]) > field["maxLength"]: raise ValidationError( "{}: default value is longer than maxLength".format(key)) elif field["type"] == "number": f_range = field["range"] if f_range[0] >= f_range[1]: raise ValidationError( "{}: range must be [lower, upper]".format(key)) if field["default"] not in range(f_range[0], f_range[1] + 1): raise ValidationError( "{}: default value is not in range {}".format( key, f_range)) elif field["type"] == "multiple": if field["default"] not in field["choices"]: raise ValidationError("{}: default value is not in {}".format( key, field["choices"])) elif field["type"] == "action": endpoint = field.get("endpoint") if endpoint: validator = URLValidator() validator.schemes = ["http", "https"] validator(field["endpoint"]) if not field["class"] in acceptable_action_classes: raise ValidationError("{}: class must be one of {}".format( key, acceptable_action_classes)) def __unicode__(self): return self.name def get_pending_invitations(self): return self.invitation_set.filter(remote=self, accepted=False, declined=False)