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

Secure API for listing users and groups #338

Closed
wants to merge 26 commits into from
Closed

Conversation

ethanstrominger
Copy link
Member

@ethanstrominger ethanstrominger commented Jun 30, 2024

Fixes #331

What changes did you make?

A new api secret-api/getusers lists users and the groups the users are assigned to. The API is authorized through an API key rather than user credentials.

  • secret_views.py: view for securely viewing all users - requires api key. Calls user_serializer with include_group = True and calls permission in secret_permissions.
  • serializer.py: add serializer for Django.contrib.groups and add include_groups paramter to UserSerializer to optionally include user groups in response
  • test_secret_api.py: tests secret based API
  • settings.py: standard changes required by djangorestframework_api_key
  • urls.py: standard changes required by djangorestframework_api_key
  • requirements.txt - added djangorestframework_api_key

Why did you make the changes (we will use this info to test)?

See issue #331 and see test_secret_api.py fir tests,

@ethanstrominger ethanstrominger changed the title Secured apis Secure API for listing users and groups Jul 12, 2024
Copy link
Member

@fyliu fyliu left a comment

Choose a reason for hiding this comment

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

Looks like you're trying to implement your own simple security protocol of "does the requesting client have the global secret key?". That doesn't seem very secure.

Can you comment on what makes this implementation secure? And why not just use Cognito since we already have that?

app/.env.docker-example Outdated Show resolved Hide resolved
app/core/tests/test_secret_api.py Outdated Show resolved Hide resolved
app/core/tests/test_secret_api.py Outdated Show resolved Hide resolved
app/core/api/secret_permissions.py Outdated Show resolved Hide resolved
Copy link
Member

@fyliu fyliu left a comment

Choose a reason for hiding this comment

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

Thanks for adding the code to support API keys!

I found a few problems, but the thing that's blocking my ability to do the review is I need confirmation for what part of the User model it's okay to return in the endpoint. I remember Bonnie said PracticeArea and ProgramArea are public in this comment.

@@ -37,6 +38,8 @@
router.register(
r"stack-element-types", StackElementTypeViewSet, basename="stack-element-type"
)
router.register(r"apikey/getusers", ApiKeyUserViewSet, basename="apikey-getusers")
router.register(r"sdgs", SdgViewSet, basename="sdg")
Copy link
Member

Choose a reason for hiding this comment

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

Looks like a duplicate line got added here?


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY")


Copy link
Member

@fyliu fyliu Sep 23, 2024

Choose a reason for hiding this comment

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

Adding an extra blank line? Possibly a merging artifact?

@@ -30,6 +30,7 @@ djangorestframework==3.14.0
# via
# drf-jwt
# drf-spectacular
djangorestframework-api-key>=3.0
Copy link
Member

Choose a reason for hiding this comment

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

We're using pip-compile (actually the uv version of it) to generate requirements.txt from requirements.in . You can add the dependency to requirements.in and run this command to generate requirements.txt.

docker-compose exec web uv pip compile requirements.in -o requirements.txt --no-header 

There's documentation on how to use it for upgrading dependencies. The command above is just missing the --upgrade flag.

@@ -59,6 +60,7 @@ platformdirs==4.2.0
# via black
pluggy==1.4.0
# via pytest
pre-commit==3.7.1
Copy link
Member

Choose a reason for hiding this comment

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

Why do you want to install pre-commit inside the docker container?

Comment on lines +49 to +50
queryset = PracticeArea.objects.all()
serializer_class = PracticeAreaSerializer # HasAPIKey checks against keys stored in
Copy link
Member

Choose a reason for hiding this comment

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

These 2 lines don't seem to do anything and they're soon replaced by the next 2 code lines of User objects and UserSerializer. Maybe you meant to remove them?

queryset = User.objects.all()

# when instantiated, get_serializer_context will be called
serializer_class = UserSerializer
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure you should be returning all the User data here rather than a subset that doesn't contain personally identifiable information (PII)?
What I mean is UserSerializer is exposing all the data.

I don't know if this PR is dependent on the user permission changes to somehow limit the exposed data fields, but the code right now will return all user table data, which doesn't seem like a good idea.

Copy link
Member

Choose a reason for hiding this comment

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

This probably should have been settled in the planning stage of this feature rather than during the PR review.

To clarify further, the problem I see is this new API key authentication method allows the API client to view user data without any of the limitations that are applied to permission-tiered users.

These are the fields it returns for user:

        fields = (
            "uuid",
            "username",
            "created_at",
            "updated_at",
            "email",
            "first_name",
            "last_name",
            "gmail",
            "preferred_email",
            "current_job_title",
            "target_job_title",
            "current_skills",
            "target_skills",
            "linkedin_account",
            "github_handle",
            "slack_id",
            "phone",
            "texting_ok",
            "time_zone",
            "groups",
        )

I'm thinking that KnowledgeBase doesn't really need or care about a user's phone number to function.

So I think we need to consult the "permissions team" (@ExperimentsInHonesty @Neecolaa) to figure out/negotiate what data this should include based on what data KnowledgeBase requires to function.

This seems to mean adding more/duplicate work to the "permissions" team (@ExperimentsInHonesty and @Neecolaa) to define exactly what tables and fields this new method can allow an API client to access. Would it be better to say the API client has the same permissions as one of the already-defined user permissions tier such as unverifiedUser or stakeholder API? In that case, wouldn't it be simpler to just use a service account (special user account with the right permission level) instead?

For reference: here's the Field Permissions sheet being worked on currently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ✅Done
Development

Successfully merging this pull request may close these issues.

Create API for KB to read, update, create, and deactivate users and read groups.
2 participants