Skip to content

Commit

Permalink
Merge branch 'main' into LocalDevEnhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
rohan-chaturvedi authored Oct 12, 2023
2 parents 769c39e + cc83ad3 commit 7e78bda
Show file tree
Hide file tree
Showing 161 changed files with 15,113 additions and 2,260 deletions.
2 changes: 1 addition & 1 deletion .env.dev.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ HTTP_PROTOCOL=http://
NEXTAUTH_URL=http://localhost
OAUTH_REDIRECT_URI=http://localhost
BACKEND_API_BASE=http://backend:8000
NEXT_PUBLIC_BACKEND_API_BASE=http://localhost/ph-backend
NEXT_PUBLIC_BACKEND_API_BASE=https://localhost/service
NEXT_PUBLIC_NEXTAUTH_PROVIDERS=google,github,gitlab

# WARNING: Replace this with a cryptographically strong random value. You can use `openssl rand -hex 32` to generate this.
Expand Down
8 changes: 6 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"package-lock.json": true
},
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.formatOnSave": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": [
"source.addMissingImports",
"source.fixAll.eslint"
Expand All @@ -21,5 +21,9 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.ignorePath": ".gitignore" // Don't run prettier for files listed in .gitignore
"prettier.ignorePath": ".gitignore",
"[python]": {
"editor.defaultFormatter": "ms-python.autopep8",
"editor.formatOnSave": true
} // Don't run prettier for files listed in .gitignore
}
4 changes: 2 additions & 2 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Phase Console - Backend

Python Django REST api + Postgres
Django + Graphene + DRF

### Generate graphql schema for frontend

```bash
./manage.py graphql_schema --schema backend.schema.schema --out ../dashboard/apollo/schema.graphql
./manage.py graphql_schema --schema backend.schema.schema --out ../frontend/apollo/schema.graphql
```
78 changes: 78 additions & 0 deletions backend/api/emails.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from django.conf import settings
from django.core.mail import send_mail
from django.template.loader import render_to_string
from datetime import datetime
import os
from api.utils import encode_string_to_base64, get_client_ip


def send_email(subject, recipient_list, template_name, context):
"""
Send email via SMTP gateway through Django's email backend.
"""
# Load the template
email_html_message = render_to_string(template_name, context)

# Get the DEFAULT_FROM_EMAIL from settings
default_from_email = getattr(settings, "DEFAULT_FROM_EMAIL")

# Send the email
send_mail(
subject,
'', # plain text content can be empty as we're sending HTML
default_from_email,
recipient_list,
html_message=email_html_message
)


def send_login_email(request, email, provider):
user_agent = request.META.get('HTTP_USER_AGENT', 'Unknown')
ip_address = get_client_ip(request)
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# Creating context dictionary
context = {
'auth': provider,
'email': email,
'ip': ip_address,
'user_agent': user_agent,
'timestamp': timestamp
}

send_email(
'New Login Alert - Phase Console',
[email],
'backend/api/email_templates/login.html',
context
)


def send_inite_email(invite):
organisation = invite.organisation.name

invited_by_social_acc = invite.invited_by.user.socialaccount_set.first()

name = invited_by_social_acc.extra_data.get('name')

if name is not None:
invited_by_name = name
else:
invited_by_name = invite.invited_by.user.email

invite_code = encode_string_to_base64(str(invite.id))

invite_link = f"{os.getenv('ALLOWED_ORIGINS')}/invite/{invite_code}"

context = {
'organisation': organisation,
'invited_by': invited_by_name,
'invite_link': invite_link
}

send_email(
f"Invite - {organisation} on Phase",
[invite.invitee_email],
'backend/api/email_templates/invite.html',
context
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Generated by Django 4.2.3 on 2023-07-31 10:52

from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
('api', '0016_organisation_plan'),
]

operations = [
migrations.CreateModel(
name='Environment',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=64)),
('env_type', models.CharField(choices=[('dev', 'Development'), ('staging', 'Staging'), ('prod', 'Production')], default='dev', max_length=7)),
('wrapped_seed', models.CharField(max_length=208)),
('wrapped_salt', models.CharField(max_length=208)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('is_deleted', models.BooleanField(default=False)),
('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.app')),
],
),
migrations.CreateModel(
name='Secret',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('collection', models.TextField(blank=True, null=True)),
('key', models.TextField()),
('key_digest', models.TextField()),
('value', models.TextField()),
('version', models.IntegerField(default=1)),
('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=64), size=10)),
('comment', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='SecretTag',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=64)),
('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.organisation')),
],
),
migrations.CreateModel(
name='SecretEvent',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('collection', models.TextField(blank=True, null=True)),
('key', models.TextField()),
('key_digest', models.TextField()),
('value', models.TextField()),
('version', models.IntegerField(default=1)),
('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=64), size=10)),
('comment', models.TextField()),
('event_type', models.CharField(choices=[('C', 'Create'), ('R', 'Read'), ('U', 'Update'), ('D', 'Delete')], default='C', max_length=1)),
('timestamp', models.BigIntegerField()),
('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')),
('secret', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.secret')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='EnvironmentSecret',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('identity_key', models.CharField(max_length=256)),
('environment_token', models.CharField(max_length=64)),
('wrapped_key_share', models.CharField(max_length=406)),
('token', models.CharField(max_length=64)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='EnvironmentKey',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('identity_key', models.CharField(max_length=256)),
('environment_token', models.CharField(max_length=64)),
('wrapped_seed', models.CharField(max_length=208)),
('wrapped_salt', models.CharField(max_length=208)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generated by Django 4.2.3 on 2023-08-01 07:57

from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
('api', '0017_environment_secret_secrettag_secretevent_and_more'),
]

operations = [
migrations.RenameField(
model_name='environmentsecret',
old_name='environment_token',
new_name='name',
),
migrations.RemoveField(
model_name='environmentkey',
name='environment_token',
),
migrations.RemoveField(
model_name='secret',
name='collection',
),
migrations.AddField(
model_name='secrettag',
name='created_at',
field=models.DateTimeField(auto_now_add=True, null=True),
),
migrations.AddField(
model_name='secrettag',
name='deleted_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='secrettag',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
migrations.CreateModel(
name='SecretFolder',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=64)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('environment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.environment')),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.secretfolder')),
],
),
migrations.AddField(
model_name='secret',
name='folder',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.secretfolder'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.3 on 2023-08-02 07:03

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('api', '0018_rename_environment_token_environmentsecret_name_and_more'),
]

operations = [
migrations.RemoveField(
model_name='secret',
name='user',
),
migrations.RemoveField(
model_name='secretevent',
name='collection',
),
migrations.AddField(
model_name='secretevent',
name='folder',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.secretfolder'),
),
]
56 changes: 56 additions & 0 deletions backend/api/migrations/0020_remove_organisation_owner_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 4.2.3 on 2023-08-04 09:30

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid

def migrate_org_owners(apps, schema_editor):
OrgModel = apps.get_model('api', 'Organisation')
OrgMemberModel = apps.get_model('api', 'OrganisationMember')

for org in OrgModel.objects.all():
OrgMemberModel.objects.create(user=org.owner, organisation=org, role='owner', identity_key=org.identity_key, created_at=org.created_at)

class Migration(migrations.Migration):

dependencies = [
('api', '0019_remove_secret_user_remove_secretevent_collection_and_more'),
]

operations = [
migrations.CreateModel(
name='OrganisationMember',
fields=[
('id', models.TextField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('role', models.CharField(choices=[('owner', 'Owner'), ('admin', 'Admin'), ('dev', 'Developer')], default='dev', max_length=5)),
('identity_key', models.CharField(blank=True, max_length=256, null=True)),
('wrapped_keyring', models.TextField(blank=True)),
('created_at', models.DateTimeField(auto_now_add=True, null=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('deleted_at', models.DateTimeField(auto_now=True)),
('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='users', to='api.organisation')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organisation', to=settings.AUTH_USER_MODEL)),
],
),
migrations.AlterField(
model_name='environmentkey',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember'),
),
migrations.AlterField(
model_name='environmentsecret',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.organisationmember'),
),
migrations.AlterField(
model_name='secretevent',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.organisationmember'),
),
migrations.RunPython(migrate_org_owners),
migrations.RemoveField(
model_name='organisation',
name='owner',
),
]
17 changes: 17 additions & 0 deletions backend/api/migrations/0021_remove_secretevent_timestamp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.3 on 2023-08-04 09:42

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('api', '0020_remove_organisation_owner_and_more'),
]

operations = [
migrations.RemoveField(
model_name='secretevent',
name='timestamp',
),
]
Loading

0 comments on commit 7e78bda

Please sign in to comment.