Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

44 add dco acceptance #45

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// Docker
"ms-azuretools.vscode-docker",
// Prettier
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
// Svelte
"svelte.svelte-vscode"
]
}
2 changes: 2 additions & 0 deletions dng_conversion/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.dng
*.jpg
18 changes: 17 additions & 1 deletion modules/odr_api/odr_api/api/endpoints/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import List
from sqlalchemy.orm import Session
from odr_core.crud import user as user_crud
from odr_core.schemas.user import User, UserCreate, UserUpdate
from odr_core.schemas.user import User, UserCreate, UserUpdate, UserDCOStatus
from odr_core.schemas.team import Team
from odr_core.database import get_db
from loguru import logger
Expand All @@ -21,6 +21,22 @@ def create_user(user: UserCreate, db: Session = Depends(get_db)):
return user_crud.create_user(db=db, user=user)


# dco
@router.post("/users/accept-dco")
def accept_dco(current_user: User = Depends(AuthProvider())):
db = next(get_db())
user_crud.update_user_dco_acceptance(db, user_id=current_user.id, accepted=True)
return {"message": "DCO accepted successfully"}


@router.get("/users/dco-status", response_model=UserDCOStatus)
def check_dco_status(current_user: User = Depends(AuthProvider())):
db = next(get_db())
dco_status = user_crud.get_user_dco_status(db, user_id=current_user.id)
return UserDCOStatus(dco_accepted=dco_status)
# end of dco


@router.get("/users/me", response_model=User)
def read_users_me(current_user: User = Depends(AuthProvider())):
return current_user
Expand Down
29 changes: 29 additions & 0 deletions modules/odr_core/odr_core/crud/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# odr_core/crud/user.py
from fastapi_sso import OpenID
from sqlalchemy.orm import Session
from sqlalchemy import update
from sqlalchemy.exc import SQLAlchemyError
from odr_core.models.user import User, UserSession
from odr_core.models.team import Team
from odr_core.schemas.user import UserCreate, UserUpdate
Expand Down Expand Up @@ -194,3 +196,30 @@ def verify_openid_user(db: Session, openid: OpenID) -> Optional[User]:
return None

return user


# DCO
def update_user_dco_acceptance(db: Session, user_id: int, accepted: bool) -> Optional[User]:
try:
stmt = (
update(User)
.where(User.id == user_id)
.values(dco_accepted=accepted, updated_at=datetime.now(timezone.utc))
.returning(User)
)
result = db.execute(stmt)
db.commit()
return result.scalar_one_or_none()
except SQLAlchemyError as e:
db.rollback()
print(f"Error updating user DCO acceptance: {e}")
return None


def get_user_dco_status(db: Session, user_id: int) -> Optional[bool]:
user = get_user(db, user_id)

if user:
return user.dco_accepted

return None
1 change: 1 addition & 0 deletions modules/odr_core/odr_core/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class User(Base):
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
dco_accepted = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
Expand Down
4 changes: 4 additions & 0 deletions modules/odr_core/odr_core/schemas/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@ class UserLogout(BaseModel):
class UserToken(BaseModel):
access_token: str
token_type: str


class UserDCOStatus(BaseModel):
dco_accepted: bool = False
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""'Add dco_accepted column to users table'

Revision ID: a72b966658fb
Revises: 266a32db1499
Create Date: 2024-09-16 18:44:44.335931

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = 'a72b966658fb'
down_revision: Union[str, None] = '266a32db1499'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.add_column('users', sa.Column('dco_accepted', sa.Boolean(), nullable=False, server_default='false'))
# ### end Alembic commands ###


def downgrade() -> None:
op.drop_column('users', 'dco_accepted')
# ### end Alembic commands ###
31 changes: 31 additions & 0 deletions modules/odr_frontend/src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,37 @@ export async function handle({ event, resolve }) {
locals.isAuthenticated = true;
locals.isSuperUser = user.is_superuser;

if (locals.isAuthenticated && !route.id?.startsWith('/dco')) {
try {
const dcoResponse = await fetch(`${API_URL}/users/dco-status`, {
headers: {
Cookie: `session=${sessionCookie}`
}
});

console.log(`DCO response status: ${dcoResponse.status}`);
console.log(`DCO response status text: ${dcoResponse.statusText}`);

if (dcoResponse.ok) {
const dcoStatus = await dcoResponse.json();

console.log('DCO status:', dcoStatus);

if (!dcoStatus.dco_accepted) {
console.log('DCO not accepted, redirecting to /dco');
throw redirect(302, '/dco');
}
} else {
console.error('Error checking DCO status');
console.error('Response:', await dcoResponse.text());
throw redirect(302, '/dco');
}
} catch (error) {
console.error('Error during DCO status check:', error);
throw redirect(302, '/dco');
}
}

if (route.id?.startsWith('/auth')) {
throw redirect(302, '/');
}
Expand Down
7 changes: 4 additions & 3 deletions modules/odr_frontend/src/routes/auth/login/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ export const actions = {

if (loginRequest.status === 200) {
event.cookies.set('session', loginResponse.id, {
expires: new Date(loginResponse.expires_at),
path: '/'
expires: new Date(loginResponse.expires_at),
path: '/',
// httpOnly: true
});
event.locals.isAuthenticated = true;
throw redirect(302, '/');
} else {
} else {
return {
status: 400,
body: JSON.stringify({ message: 'Error logging in user' })
Expand Down
36 changes: 36 additions & 0 deletions modules/odr_frontend/src/routes/dco/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { redirect, fail } from '@sveltejs/kit';
import { ENV } from '$lib/server/env';
import type { Actions } from './$types';

export const actions: Actions = {
default: async (event) => {
const formData = await event.request.formData();
const acceptDCO = formData.get('acceptDCO');

if (!acceptDCO) {
return fail(400, { message: 'You must accept the DCO to continue' });
}

let response;

try {
response = await fetch(`${ENV.API_SERVICE_URL}/users/accept-dco`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Cookie: event.request.headers.get('cookie') || ''
}
});
} catch (error) {
console.error('Network error accepting DCO:', error);
return fail(500, { message: 'A network error occurred while processing your request' });
}

if (response.ok) {
throw redirect(302, '/');
} else {
const errorData = await response.json();
return fail(response.status, { message: errorData.message || 'Failed to accept DCO' });
}
}
};
77 changes: 77 additions & 0 deletions modules/odr_frontend/src/routes/dco/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts">
import { enhance } from '$app/forms';

let acceptedDCO = false;
</script>

<svelte:head>
<title>DCO | OMI Data Pipeline</title>
</svelte:head>

<div class="flex flex-col overflow-y-auto">
<main class="container mx-auto p-4">
<h1 class="text-3xl font-bold mb-4">Developer Certificate of Origin</h1>
<h2 class="text-xl mb-2">Version 1.1</h2>

<div class="bg-gray-800 p-4 rounded-lg mb-4 whitespace-pre-wrap text-gray-200">
{`Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we cite the "open source license indicated in the file" anywhere in this screen? We might want to clarify that point. If this will be a dataset, I believe its CDLA 2.0

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hipsterusername No we don't mention the specific license anywhere.

To clarify are you suggesting updating the wording of the DCO here to add a line specifying the license, or should we just have a line above/before the DCO block stating like "The following Developer Certificate of Origin agreement covers all contributions to Open Model Initiative Datasets. All OMI datasets will fall under the CDLA 2.0 license" perhaps with a link to https://cdla.dev/permissive-2-0/ ?

Just wanting to make sure I understand the spec or expected wording updates correctly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think we just need to highlight which license applies in the UI somewhere

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay thanks, I will add the line and link in a quick subsequent PR. 👍

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.`}
</div>

<form method="POST" use:enhance>
<div class="mb-4">
<label class="flex items-center">
<input type="checkbox" bind:checked={acceptedDCO} name="acceptDCO" class="mr-2">
<span>I accept the terms of the Developer Certificate of Origin</span>
</label>
</div>
<button
type="submit"
class="bg-blue-500 text-white px-4 py-2 rounded transition-colors duration-200 ease-in-out
disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-600"
disabled={!acceptedDCO}
>
Accept DCO
</button>
</form>
</main>
</div>

<style>
.container {
max-width: 800px;
padding-bottom: 2rem;
}
</style>
Loading