class TokenCache(models.Model):
    user_object_id = models.CharField(null=True, max_length=255)
    refresh_token = EncryptedTextField(null=True)
    access_tokens = EncryptedTextField(null=True)

    class Meta:
        db_table = 'token_cache'
Exemple #2
0
class EdxEndpoint(ValidateOnSaveMixin, TimestampedModel):
    """Model that represents an edX instance to which videos will be posted"""

    name = models.CharField(max_length=20,
                            unique=True,
                            blank=False,
                            null=False)
    base_url = models.CharField(max_length=100, blank=False, null=False)
    access_token = models.CharField(max_length=2048)
    expires_in = models.IntegerField(default=0)
    hls_api_path = models.CharField(max_length=100,
                                    default=DEFAULT_EDX_HLS_API_PATH)
    is_global_default = models.BooleanField(default=False)
    collections = models.ManyToManyField("Collection",
                                         through="CollectionEdxEndpoint")

    client_id = EncryptedTextField()
    secret_key = EncryptedTextField()

    @property
    def full_api_url(self):
        """Returns the full URL of the edX API endpoint for posting videos"""
        return multi_urljoin(
            self.base_url,
            self.hls_api_path or DEFAULT_EDX_HLS_API_PATH,
            add_trailing_slash=True,
        )

    def update_access_token(self, data):
        """Saves new access token"""
        self.access_token = data["access_token"]
        self.expires_in = data["expires_in"]
        self.save()

    def refresh_access_token(self):
        """
        Checks if access token is expired, if so it sends a request to get new token
        """
        try:
            expires_in = timedelta(seconds=self.expires_in)
        except TypeError:
            response = send_refresh_request(self.base_url, self.client_id,
                                            self.secret_key)
            self.update_access_token(response)
            return

        if now_in_utc() - self.updated_at >= expires_in:
            response = send_refresh_request(self.base_url, self.client_id,
                                            self.secret_key)
            self.update_access_token(response)

    def __str__(self):
        return "{} - {}".format(self.name, self.base_url)

    def __repr__(self):
        return (
            '<EdxEndpoint: name="{self.name!r}", base_url="{self.base_url!r}">'
            .format(self=self))
Exemple #3
0
class XeroAuthFlowState(models.Model):
    """ Temporary storage for details of in-flow auth requests.
    Likely to be replaced with redis or something later on."""
    oauth_token = models.TextField(primary_key=True,
                                   help_text="OAuth token to which "
                                   "this request belongs")
    state = EncryptedTextField(help_text="JSON with all values to "
                               "recreate a  given "
                               "PublicCredentials state")
    auth_url = models.URLField(null=False,
                               blank=False,
                               max_length=4096,
                               help_text="URL where the user was sent to "
                               "authenticate")
    next_page = models.CharField(max_length=255,
                                 null=True,
                                 blank=False,
                                 help_text="Where to redirect once successful")

    created_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.created_on.isoformat() + ' ' + \
               json.loads(self.state)['oauth_token']

    @classmethod
    def start_flow(cls, acceptance_url, next_page=None):
        """
        Start authorization flow
        """
        # instantiating credentials automatically starts the flow
        creds = PublicCredentials(get_xero_consumer_key(),
                                  get_xero_consumer_secret(), acceptance_url)
        # save state for later
        af_state = cls(state=json.dumps(creds.state, cls=DjangoJSONEncoder),
                       oauth_token=creds.oauth_token,
                       next_page=next_page)
        af_state.auth_url = creds.url
        af_state.save()
        return af_state

    def complete_flow(self, verification_code, user):
        """ Complete Authorization flow
        Note that you must already have a Django user, since Xero won't tell you
        anything about the logged-on user.

        :param verification_code: code to verify the original request
        :param user: User instance
        :returns XeroUser instance
        """
        # rebuild our connection
        state_dict = json.loads(self.state, object_hook=_datetime_parser_hook)
        creds = PublicCredentials(**state_dict)
        creds.verify(verification_code)
        xero_user = XeroUser.from_state(creds, user)
        return xero_user
Exemple #4
0
class Subject(models.Model):
    SEX_OPTIONS = [('F', 'Female'), ('M', 'Male'), ('UN', 'Undeclared')]

    ETHNICITY_OPTIONS = [('HL', 'Hispanic or Latino'),
                         ('NHL', 'Not Hispanic or Latino'),
                         ('UN', 'Undeclared')]

    RACE_OPTIONS = [('AIAN', 'American Indian or Alaska Native'),
                    ('A', 'Asian'), ('B', 'Black or African American'),
                    ('NHPI', 'Native Hawaiian or Other Pacific Islander'),
                    ('W', 'White'), ('UN', 'Undeclared')]

    ID_ORIGINS = [('PYENS', 'PyEnsemble'), ('PRLFC', 'Prolific')]

    subject_id = models.CharField(primary_key=True, max_length=32)
    id_origin = models.CharField(max_length=12,
                                 choices=ID_ORIGINS,
                                 default='PYENS')
    date_entered = models.DateField(auto_now_add=True)
    name_last = EncryptedCharField(max_length=24)
    name_first = EncryptedCharField(max_length=24)
    name_middle = EncryptedCharField(max_length=24)
    name_suffix = EncryptedCharField(max_length=24)
    passphrase = EncryptedCharField(max_length=64)
    security_questions = models.TextField()
    security_responses = EncryptedTextField(max_length=128)
    email = EncryptedEmailField(max_length=48)
    phone1 = EncryptedCharField(max_length=16)
    phone2 = EncryptedCharField(max_length=16)
    address1 = EncryptedCharField(max_length=24)
    address2 = EncryptedCharField(max_length=24)
    address3 = EncryptedCharField(max_length=24)
    city = EncryptedCharField(max_length=24)
    county = EncryptedCharField(max_length=24)
    state = EncryptedCharField(max_length=24)
    postal_code = EncryptedCharField(max_length=10)
    sex = models.CharField(
        max_length=2,
        choices=SEX_OPTIONS,
        default='UN',
    )
    ethnicity = models.CharField(
        max_length=4,
        choices=ETHNICITY_OPTIONS,
        default='UN',
    )
    race = models.CharField(
        max_length=4,
        choices=RACE_OPTIONS,
        default='UN',
    )
    dob = EncryptedDateField(default=datetime(1900, 1, 1))
    notes = models.TextField()
Exemple #5
0
class Message(models.Model):
    dialog = models.ForeignKey(Dialog,
                               related_name="messages",
                               on_delete=models.DO_NOTHING)
    sender = models.ForeignKey(User,
                               verbose_name=_("Author"),
                               related_name="messages",
                               on_delete=models.DO_NOTHING)
    text = EncryptedTextField()
    read = models.BooleanField(verbose_name=_("Read"), default=False)
    time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.sender.username + "in " + self.dialog + "(" + str(
            self.time) + ") - '" + self.text + "'"
Exemple #6
0
class XeroSecret(models.Model):
    """
    Configuration data that has to be protected.
    """
    name = models.CharField(max_length=255,
                            primary_key=True,
                            help_text="Key to refer to this secret")
    value = EncryptedTextField(blank=True,
                               default='',
                               help_text="Value to store "
                               "(will be encrypted in database)")
    label = models.CharField(max_length=255,
                             blank=True,
                             null=True,
                             help_text='Human-readable label or description')

    def __str__(self):
        return self.name
Exemple #7
0
class MinterWallets(models.Model):
    objects = BulkUpdateManager()

    user = models.OneToOneField(User,
                                verbose_name='Владелец кошелька',
                                on_delete=models.CASCADE,
                                related_name='wallet')
    address = models.CharField(max_length=42, verbose_name='Адрес (Mx...)')
    mnemonic = EncryptedTextField(verbose_name='Сид фраза')

    balances = JSONField(verbose_name='Баланс', default=dict)
    balance_updated_at = models.DateTimeField(
        verbose_name='Последнее обновление баланса', auto_now=True)

    def __str__(self):
        return '{name}'.format(name=self.user)

    class Meta:
        verbose_name = 'Minter-Кошелек'
        verbose_name_plural = 'Minter-Кошельки'

    @property
    def balance(self):
        balance = defaultdict(Decimal)
        for coin, value in self.balances.items():
            dvalue = Decimal(value)
            if truncate(dvalue, 4) > 0:
                balance[coin] = dvalue
        if not balance:
            balance['TIME'] = Decimal(0)
        return balance

    def setbalance(self, coin, value):
        self.balances[coin] = str(value)

    @property
    def balance_formatted(self):
        return '\n'.join(f'{truncate(balance, 4)} {coin}'
                         for coin, balance in self.balance.items()
                         if balance > 0)

    @property
    def minter_wallet(self):
        return MinterWallet.create(mnemonic=self.mnemonic)
Exemple #8
0
class ChatWallet(models.Model):
    objects = BulkUpdateManager()

    chat = models.ForeignKey(AllowedChat,
                             verbose_name='Чат',
                             on_delete=models.CASCADE)
    address = models.CharField(max_length=42,
                               verbose_name='Адрес (Mx...)',
                               null=True,
                               default=None)
    mnemonic = EncryptedTextField(verbose_name='Сид фраза', default='')

    balances = JSONField(verbose_name='Баланс', default=dict)
    balance_updated_at = models.DateTimeField(
        verbose_name='Последнее обновление баланса', auto_now=True)

    def __str__(self):
        return '"{title}" chat wallet'.format(title=self.chat.title_chat)

    class Meta:
        verbose_name = 'Кошелек чата'
        verbose_name_plural = 'Кошельки чатов'

    @property
    def balance(self):
        balance = defaultdict(Decimal)
        for coin, value in self.balances.items():
            dvalue = Decimal(value)
            if truncate(dvalue, 4) > 0:
                balance[coin] = dvalue
        if not balance:
            balance[self.chat.coin] = Decimal(0)
        return balance

    def setbalance(self, coin, value):
        self.balances[coin] = str(value)

    @property
    def balance_formatted(self):
        return '\n'.join(f'{truncate(balance, 4)} {coin}'
                         for coin, balance in self.balance.items())
Exemple #9
0
class Tools(models.Model):
    address = models.CharField(verbose_name='Адрес выплат',
                               max_length=42,
                               default='')
    mnemonic = EncryptedTextField(verbose_name='Seed-фраза')
    payload = models.CharField(
        verbose_name='Payload при выводе средств из бота',
        default='',
        max_length=80)

    members_limit = models.PositiveIntegerField(
        verbose_name=
        'Число участников, при котором можно считать чат "большим"',
        default=1000)
    user_limit_day = models.DecimalField(
        decimal_places=6,
        max_digits=24,
        verbose_name='Лимит таймов на одного юзера, в день',
        default=5)
    chat_limit_day = models.DecimalField(
        decimal_places=6,
        max_digits=24,
        verbose_name='Лимит таймов на чат, в день',
        default=200)
    total_limit_day = models.DecimalField(
        decimal_places=6,
        max_digits=24,
        verbose_name='Общий лимит таймов на всех, в день',
        default=4000)

    coin = models.CharField(verbose_name='Монета,в к-ой идет выплата',
                            default='TIME',
                            max_length=10)

    class Meta:
        verbose_name = 'Конфиг: выплаты, константы, параметры'
Exemple #10
0
class Site(models.Model):
    """Description of the local or a remote site."""

    #: DateTime of creation.
    date_created = models.DateTimeField(auto_now_add=True,
                                        help_text="DateTime of creation")
    #: DateTime of last modification.
    date_modified = models.DateTimeField(
        auto_now=True, help_text="DateTime of last modification")

    #: Record UUID.
    sodar_uuid = models.UUIDField(default=uuid_object.uuid4,
                                  unique=True,
                                  help_text="Record SODAR UUID")

    consortia = models.ManyToManyField(
        Consortium,
        related_name="sites",
        through="ConsortiumMember",
        help_text="Consortia that the site is a member of",
    )

    LOCAL = "local"
    REMOTE = "remote"
    ROLE_CHOICES = (
        (LOCAL, LOCAL),
        (REMOTE, REMOTE),
    )

    ENABLED = "enabled"
    DISABLED = "disabled"
    STATE_CHOICES = (
        (ENABLED, ENABLED),
        (DISABLED, DISABLED),
    )

    AES_128_CBC = "aes128-cbc"
    AES_192_CBC = "aes192-cbc"
    AES_256_CBC = "aes256-cbc"
    KEY_ALGOS_SYMMETRIC = (AES_128_CBC, AES_192_CBC, AES_256_CBC)
    RSA_SHA256 = "rsa-sha256"
    RSA_SHA512 = "rsa-sha512"
    ECDSA_SHA256 = "ecdsa-sha256"
    ECDSA_SHA512 = "ecdsa-sha512"
    KEY_ALGOS_ASYMMETRIC = (RSA_SHA256, RSA_SHA512, ECDSA_SHA256, ECDSA_SHA512)
    KEY_ALGO_CHOICES = (
        (RSA_SHA256, RSA_SHA256),
        (RSA_SHA512, RSA_SHA512),
        (ECDSA_SHA256, ECDSA_SHA256),
        (ECDSA_SHA512, ECDSA_SHA512),
    )

    role = models.CharField(max_length=100,
                            choices=ROLE_CHOICES,
                            help_text="Site role.")

    state = models.CharField(max_length=100,
                             choices=STATE_CHOICES,
                             help_text="Site state.")

    identifier = models.CharField(
        max_length=128,
        unique=True,
        null=False,
        blank=False,
        help_text="Site name (in reverse DNS notation)",
    )
    title = models.CharField(max_length=128,
                             null=False,
                             blank=False,
                             help_text="Title of the site")
    description = models.TextField(
        null=True, blank=True, help_text="Optional description of the site")

    entrypoint_url = models.TextField(null=False,
                                      blank=False,
                                      help_text="Site base URL")

    key_algo = models.CharField(max_length=64,
                                choices=KEY_ALGO_CHOICES,
                                help_text="Key algorithm to use")

    max_clock_skew = models.IntegerField(
        default=(5 * 60),
        help_text="Maximal age of request based on date header")

    def is_key_algo_symmetric(self):
        return self.key_algo in self.KEY_ALGOS_SYMMETRIC

    private_key = EncryptedTextField(
        null=True,
        blank=True,
        help_text="(Private) key for (a)symmetric encryption.")
    public_key = models.TextField(
        null=True,
        blank=True,
        help_text="Public key for asymmetric encryption.")

    def get_all_projects(self):
        # TODO: speed this up with raw SQL query
        result = {}
        for consortium in self.consortia.all():
            for project in consortium.projects.all():
                if project.pk not in result:
                    result[project.pk] = project
        return result.values()

    def public_key_fingerprints(self):
        k = RSA.import_key(self.public_key)
        sha256digest = hashlib.sha256(k.exportKey("DER", pkcs=8)).hexdigest()
        sha1digest = hashlib.sha1(k.exportKey("DER", pkcs=8)).hexdigest()
        md5digest = hashlib.md5(k.exportKey("DER", pkcs=8)).hexdigest()
        return "\n".join([
            "md5:%s" % _insert_char_every_n_chars(md5digest, ":", 2),
            "sha1:%s" % _insert_char_every_n_chars(sha1digest, ":", 2),
            "sha256:%s" % _insert_char_every_n_chars(sha256digest, ":", 2),
        ])

    def validate_unique(self, *args, **kwargs):
        super().validate_unique(*args, **kwargs)
        if self.role == Site.LOCAL and self.state == Site.ENABLED:
            local_site_pks = [
                s.pk for s in Site.objects.filter(state=Site.ENABLED,
                                                  role=Site.LOCAL)
            ]
            if local_site_pks and self.pk not in local_site_pks:
                raise ValidationError(
                    "There must be at most one active local site!")
        if (self.role == Site.LOCAL
                and self.key_algo not in Site.KEY_ALGOS_SYMMETRIC
                and not self.public_key):
            raise ValidationError(
                "both public and private key are needed for local sites for RSA/ECDSA."
            )

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("beaconsite:site-detail",
                       kwargs={"site": self.sodar_uuid})
Exemple #11
0
class Account(AbstractBaseUser):
    # Makes unique id using uuid ver. 4 (may be switched to ver. 5)  a 32 bit alphanumeric key generated from a random 128-bit number
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField(verbose_name="email", max_length=60, unique=True)
    # email = EncryptedEmailField(verbose_name="email", max_length=60, unique=True)
    # Uses fernet encryption, which combines 128-bit AES encryption with SHA-256 hashing
    first_name = EncryptedCharField(max_length=20)
    middle_name = EncryptedCharField(max_length=20, null=True, blank=True)
    last_name = EncryptedCharField(max_length=20)
    username = models.CharField(max_length=20, unique=True)
    # Charfield because zip codes can start with 0, Integers can't hold leading 0's
    street = EncryptedTextField()
    city = EncryptedCharField(max_length=45)
    state = EncryptedCharField(max_length=2, choices=STATE_CHOICES)
    zip_code = EncryptedCharField(max_length=5, blank=True, null=True)
    dob = EncryptedDateField(verbose_name="date of birth",
                             auto_now=False,
                             auto_now_add=False)
    citizen = EncryptedCharField(max_length=5,
                                 default=True,
                                 choices=BOOLEAN_CHOICES)
    # ssn = EncryptedCharField(max_length=9, unique=True)
    ssn = EncryptedCharField(max_length=9, blank=True, null=True)
    gender = models.CharField(max_length=23,
                              choices=GENDER_CHOICES,
                              default="Prefer Not To Disclose")
    disqualified = EncryptedCharField(max_length=5,
                                      default=False,
                                      choices=BOOLEAN_CHOICES)
    restored = EncryptedCharField(max_length=5,
                                  default=False,
                                  null=True,
                                  blank=True,
                                  choices=BOOLEAN_CHOICES)
    active_mil = EncryptedCharField(max_length=5, choices=BOOLEAN_CHOICES)
    sig = EncryptedCharField(max_length=5,
                             default=False,
                             choices=BOOLEAN_CHOICES)

    # Required for AbstractBaseUser
    date_joined = models.DateTimeField(verbose_name='date joined',
                                       auto_now_add=True)
    last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)

    # Defines what is used for authentication and what is required
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = [
        'first_name', 'last_name', 'username', 'zip_code', 'dob', 'street',
        'city', 'state', 'gender', 'disqualified', 'active_mil', 'sig'
    ]

    objects = MyAccountManager()

    def __str__(self):
        return self.email

    # Only allows certain permissions if user is admin
    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True
Exemple #12
0
class XeroUser(models.Model):
    """ Xero account linked to a User """
    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                on_delete=CASCADE,
                                null=False,
                                blank=False,
                                help_text="Django user")
    org = models.CharField(max_length=255,
                           blank=True,
                           null=True,
                           help_text="Identifier for the org the user belongs "
                           "to, in theory")
    last_token = EncryptedTextField(
        blank=True,
        default='',
        help_text="JSON with last successful login "
        "details (so you can check if "
        "still logged on). ")
    xero_id = models.CharField(max_length=255,
                               blank=True,
                               null=True,
                               help_text="User ID in Xero. "
                               "Note that this has to be manually "
                               "retrieved with custom logic, because "
                               "there is no way to find it from "
                               "a token; so it's blank by default.")
    xero_email = models.EmailField(max_length=255,
                                   blank=True,
                                   null=True,
                                   help_text="Email registered with Xero. "
                                   "If present, it overrides "
                                   "User.email when dealing with Xero")
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.user.first_name} {self.user.last_name}"

    @classmethod
    def from_state(cls, creds: PublicCredentials, user):
        """ given a token reference, retrieve or construct a XeroUser instance.
        Note that you must already have a Django user, since Xero won't tell you
        anything about the logged-on user.
        :param creds: PublicCredentials instance with a valid session
        :param user: User instance
        :returns: XeroUser instance
        """
        if not creds.verified:
            raise Exception("Trying to create a XeroUser with "
                            "an invalid session")

        xero_user, created = cls.objects.get_or_create(user=user)
        xero_user.last_token = json.dumps(creds.state, cls=DjangoJSONEncoder)
        xero_user.save()
        return xero_user

    @property
    def token(self):
        """
        Get a dict with the current token info
        :return: dict
        """
        return json.loads(self.last_token, object_hook=_datetime_parser_hook)

    @property
    def client(self):
        """
        Get a ready-made xero.Xero object
        :return: xero.Xero instance
        """
        return Xero(credentials=PublicCredentials(**self.token),
                    user_agent=get_xero_consumer_key())

    def guess_user_details(self):
        """
        Xero provides no way to find user details from a oauth1.0 token, but
        if we have a prepopulated user we can make an educated guess.
        This is not foolproof, of course, which is why we don't automatically
        set the xero_id field.
        Use at your own risk.
        :return: dict with user details that we *think* might be from the user
                who generated the token, or None if not found anything
        """
        filters = [
            # django_field_name, xero_field_name
            ('email', 'emailaddress'),
            ('first_name', 'firstname'),
            ('last_name', 'lastname')
        ]

        params = {
            xerokey: getattr(self.user, djkey)
            for djkey, xerokey in filters
        }

        if self.xero_email:
            params['emailaddress'] = self.xero_email

        while True:
            result = self.client.users.filter(**params)
            if len(result) > 0:
                return result[0]

            if params.get('emailaddress', None):
                # try again with just the email
                params.pop('firstname')
                params.pop('lastname')
            else:
                break

        return None

    def _request_data(self, verb, url, **kwargs):
        """
        Utility for authenticated calls to Xero apis not yet supported by
        pyxero, getting json back. Note that pagination is NOT handled.

        :param verb: 'get','post',...
        :param url: url to call
        :param kwargs: extra parameters to pass to requests
        :return: list of returned json dicts
        """
        result = self._request(verb, url, **kwargs)
        if result.status_code != 200:
            raise Exception(f"Unexpected response: "
                            f"{result.status_code} {result.text}\n"
                            f"Call was: {verb} {url}\n"
                            f"Args: {kwargs}")
        data = result.json()
        if settings.DEBUG:
            from pprint import pprint
            pprint(data)
        return data

    def _request(self, verb, url, **kwargs):
        """
        Utility for authenticated calls to Xero apis not yet supported by pyxero
        :param verb: 'get','post',...
        :param url: url to call
        :param kwargs: extra parameters to pass to requests
        :return: list of returned json dicts
        """
        creds = self.client.accounts.credentials
        try:
            result = getattr(requests, verb.lower())(url,
                                                     auth=creds.oauth,
                                                     headers={
                                                         'User-Agent':
                                                         creds.consumer_key
                                                     },
                                                     **kwargs)
            return result
        except Exception as e:
            logger.exception(e)
            return None