class Fixed(self.target_class):
     @extend_schema(
         operation_id="reset_password_confirm",
         request={"application/json": PasswordResetConfirmSerializer},
         responses={
             status.HTTP_200_OK: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Success",
                 examples=[
                     OpenApiExample(
                         "Successful password reset",
                         value={"detail": "Password has been reset with the new password."},
                     )
                 ],
             ),
             status.HTTP_400_BAD_REQUEST: OpenApiResponse(
                 PasswordResetConfirmSerializer,
                 description="Invalid input",
                 examples=[
                     OpenApiExample(
                         "Invalid uid",
                         value={"uid": "Invalid value"},
                         status_codes=[f"{status.HTTP_400_BAD_REQUEST}"],
                     )
                 ],
             ),
         },
         tags=["auth"],
     )
     def post(self, request, *args, **kwargs):
         pass
def id_or_type_parameter(name="id_or_type"):
    return OpenApiParameter(
        name,
        OpenApiTypes.STR,
        OpenApiParameter.PATH,
        required=True,
        description="A UUID or type string identifying the form.",
        examples=[
            OpenApiExample(
                "UUID",
                "3fa85f64-5717-4562-b3fc-2c963f66afa6",
            ),
            OpenApiExample("Hacker Application Type", "hacker_application"),
        ])
 class Fixed(self.target_class):
     @extend_schema(
         operation_id="register",
         request={"application/json": RegisterSerializer},
         responses={
             status.HTTP_200_OK: OpenApiResponse(
                 response_serializer,
                 description="successful operation",
                 examples=[
                     OpenApiExample(
                         "Successful registration",
                         value={"detail": "Verification e-mail sent."},
                         status_codes=[f"{status.HTTP_200_OK}"],
                     )
                 ],
             ),
             status.HTTP_400_BAD_REQUEST: OpenApiResponse(
                 RegisterSerializer,
                 description="Invalid input",
                 examples=[
                     OpenApiExample(
                         "Invalid email",
                         value={"email": "Enter a valid email address."},
                         status_codes=[f"{status.HTTP_400_BAD_REQUEST}"],
                     ),
                     OpenApiExample(
                         "Invalid password",
                         value={"password2": "This password is too common."},
                         status_codes=[f"{status.HTTP_400_BAD_REQUEST}"],
                     ),
                 ],
             ),
             status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Unauthorized",
                 examples=[
                     OpenApiExample(
                         "Invalid auth header",
                         description="Attempting to register with the Authorization header already set",
                         value={"detail": "Invalid token."},
                         status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                     )
                 ],
             ),
         },
         tags=["auth"],
     )
     def post(self, request, *args, **kwargs):
         pass
Exemple #4
0
class ProjectRuleStatsIndexEndpoint(RuleEndpoint):
    private = True

    @extend_schema(
        operation_id=
        "Retrieve firing starts for an issue alert rule for a given time range. Results are returned in hourly buckets.",
        parameters=[
            GLOBAL_PARAMS.ORG_SLUG, GLOBAL_PARAMS.PROJECT_SLUG,
            ISSUE_ALERT_PARAMS
        ],
        responses={
            200: TimeSeriesValueSerializer,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[
            OpenApiExample(
                "Successful response",
                value={},
                status_codes=["200"],
            )
        ],
    )
    def get(self, request: Request, project: Project, rule: Rule) -> Response:
        start, end = get_date_range_from_params(request.GET)
        results = fetch_rule_hourly_stats(rule, start, end)
        return Response(
            serialize(results, request.user, TimeSeriesValueSerializer()))
Exemple #5
0
class ProjectRuleGroupHistoryIndexEndpoint(RuleEndpoint):
    @extend_schema(
        operation_id="Retrieve a group firing history for an issue alert",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, GLOBAL_PARAMS.PROJECT_SLUG, ISSUE_ALERT_PARAMS],
        responses={
            200: RuleGroupHistorySerializer,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[
            OpenApiExample(
                "Successful response",
                value={},
                status_codes=["200"],
            )
        ],
    )
    def get(self, request: Request, project: Project, rule: Rule) -> Response:
        per_page = self.get_per_page(request)
        cursor = self.get_cursor_from_request(request)
        start, end = get_date_range_from_params(request.GET)

        results = fetch_rule_groups_paginated(rule, start, end, cursor, per_page)

        response = Response(serialize(results.results, request.user, RuleGroupHistorySerializer()))
        self.add_cursor_headers(request, response, results)
        return response
 class Fixed(self.target_class):
     @extend_schema(
         operation_id="change_password",
         request={"application/json": PasswordChangeSerializer},
         responses={
             status.HTTP_200_OK: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Success",
                 examples=[
                     OpenApiExample(
                         "Successful password change",
                         value={"detail": "New password has been saved."},
                         status_codes=[f"{status.HTTP_200_OK}"],
                     )
                 ],
             ),
             status.HTTP_400_BAD_REQUEST: OpenApiResponse(
                 PasswordChangeSerializer,
                 description="Invalid input",
                 examples=[
                     OpenApiExample(
                         "Password too common",
                         value={"new_password2": "This password is too common."},
                         status_codes=[f"{status.HTTP_400_BAD_REQUEST}"],
                     )
                 ],
             ),
             status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Unauthorized",
                 examples=[
                     OpenApiExample(
                         "No token",
                         description="Invalid or missing Authorization header",
                         value={"detail": "Authentication credentials were not provided."},
                         status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                     )
                 ],
             ),
         },
         tags=["auth"],
     )
     def post(self, request, *args, **kwargs):
         pass
 class Fixed(self.target_class):
     @extend_schema(
         operation_id="login",
         request={"application/json": LoginSerializer},
         responses={
             status.HTTP_200_OK: OpenApiResponse(
                 get_token_serializer_class(),
                 description="Successful login",
                 examples=[
                     OpenApiExample(
                         "Success",
                         value={"key": "491484b928d4e497ef3359a789af8ac204fc96db"},
                         status_codes=[f"{status.HTTP_200_OK}"],
                     )
                 ],
             ),
             status.HTTP_400_BAD_REQUEST: OpenApiResponse(
                 NonFieldErrorResponseSerializer,
                 description="Invalid input",
                 examples=[
                     OpenApiExample(
                         "Invalid credentials",
                         value={"non_field_errors": "Unable to log in with provided credentials"},
                         status_codes=[f"{status.HTTP_400_BAD_REQUEST}"],
                     )
                 ],
             ),
             status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Unauthorized",
                 examples=[
                     OpenApiExample(
                         "Invalid auth header",
                         description="Attempting to login with the Authorization header already set",
                         value={"detail": "Invalid Token."},
                         status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                     )
                 ],
             ),
         },
         tags=["auth"],
     )
     def post(self, request, *args, **kwargs):
         pass
Exemple #8
0
class RegisterView(generics.GenericAPIView):
    serializer_class = RegisterSerializer

    @extend_schema(tags=["Register"],
                   summary="user 등록",
                   examples=[
                       OpenApiExample(request_only=True,
                                      summary="sample 1",
                                      name="sample 1",
                                      value={
                                          "email": "*****@*****.**",
                                          "username": "******",
                                          "password": "******"
                                      }),
                       OpenApiExample(response_only=True,
                                      summary="sample 1",
                                      name="sample 1",
                                      value={
                                          "username": "******",
                                          "email": "*****@*****.**",
                                      }),
                   ])
    def post(self, request: Request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        user: User = serializer.save()
        token = RefreshToken.for_user(user).access_token
        current_site = get_current_site(request).domain
        relative_link = reverse('email-verify')

        abs_url = 'http://' + current_site + relative_link + "?token=" + str(
            token)
        email_body = 'Hi ' + user.username + \
                     ' Use the link below to verify your email \n' + abs_url
        data = {
            'email_body': email_body,
            'to_email': user.email,
            'email_subject': 'Verify your email'
        }

        # Util.send_email(data)
        print("token : ", token)

        return Response(serializer.data, status=status.HTTP_201_CREATED)
 class Fixed(self.target_class):
     @extend_schema(
         operation_id="reset_password",
         request={"application/json": PasswordResetSerializer},
         responses={
             status.HTTP_200_OK: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Success",
                 examples=[
                     OpenApiExample(
                         "Success",
                         value={"detail": "Password reset e-mail has been sent."},
                         status_codes=[f"{status.HTTP_200_OK}"],
                     )
                 ],
             ),
             status.HTTP_400_BAD_REQUEST: OpenApiResponse(
                 PasswordResetSerializer,
                 description="Invalid input",
                 examples=[
                     OpenApiExample(
                         "User with email doesn't exist",
                         value={"email": "The e-mail address is not assigned to any user account"},
                         status_codes=[f"{status.HTTP_400_BAD_REQUEST}"],
                     )
                 ],
             ),
             status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Unauthorized",
                 examples=[
                     OpenApiExample(
                         "Invalid auth header",
                         description="Attempting to reset password with the Authorization header already set",
                         value={"detail": "Invalid token."},
                         status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                     )
                 ],
             ),
         },
         tags=["auth"],
     )
     def post(self, request, *args, **kwargs):
         pass
Exemple #10
0
 class Fixed(self.target_class):
     @extend_schema(
         operation_id="verify_email",
         request={"application/json": VerifyEmailSerializer},
         responses={
             status.HTTP_200_OK: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Success",
                 examples=[OpenApiExample("Successful email verification", value={"detail": "ok"})],
             ),
             status.HTTP_404_NOT_FOUND: OpenApiResponse(
                 DetailResponseSerializer,
                 description="Not found",
                 examples=[OpenApiExample("Invalid key", value={"detail": "Not found."})],
             ),
         },
         tags=["auth"],
     )
     def post(self, request, *args, **kwargs):
         pass
Exemple #11
0
        class Fixed(self.target_class):
            @extend_schema(operation_id="logout", **get_schema_params, tags=["auth"])
            def get(self, request, *args, **kwargs):
                pass

            @extend_schema(
                operation_id="logout",
                request=None,
                responses={
                    status.HTTP_200_OK: OpenApiResponse(
                        DetailResponseSerializer,
                        description="Success",
                        examples=[
                            OpenApiExample(
                                "Success",
                                value={"detail": "Successfully logged out."},
                                status_codes=[f"{status.HTTP_200_OK}"],
                            )
                        ],
                    ),
                    status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                        DetailResponseSerializer,
                        description="Unauthorized",
                        examples=[
                            OpenApiExample(
                                "No token",
                                value={"detail": "Invalid token header. No credentials provided."},
                                status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                            )
                        ],
                    ),
                },
                tags=["auth"],
            )
            def post(self, request, *args, **kwargs):
                pass
Exemple #12
0
class SiaeViewSet(viewsets.ReadOnlyModelViewSet):
    """
    # Liste des SIAE

    La plateforme renvoie une liste de SIAE à proximité d’une ville (déterminée par son code INSEE)
    et dans un rayon de recherche en kilomètres autour du centre de cette ville.

    Les coordonnées des centre villes sont issus de [https://geo.api.gouv.fr](https://geo.api.gouv.fr/)


    Chaque SIAE est accompagnée d’un certain nombre de métadonnées :

     - SIRET
     - Type
     - Raison Sociale
     - Enseigne
     - Site web
     - Description de la SIAE
     - Blocage de toutes les candidatures OUI/NON
     - Adresse de la SIAE
     - Complément d’adresse
     - Code Postal
     - Ville
     - Département

    Chaque SIAE peut proposer 0, 1 ou plusieurs postes. Pour chaque poste renvoyé, les métadonnées fournies sont :

     - Appellation ROME
     - Date de création
     - Date de modification
     - Recrutement ouvert OUI/NON
     - Description du poste
     - Appellation modifiée
    """

    serializer_class = SiaeSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = SiaeOrderingFilter
    ordering = ["id"]

    # No authentication is required on this API and everybody can query anything − it’s read-only.
    authentication_classes = []
    permission_classes = []

    NOT_FOUND_RESPONSE = OpenApiExample(
        "Not Found",
        description="Not Found",
        value="Pas de ville avec pour code_insee 1234",
        response_only=True,
        status_codes=["404"],
    )
    sort_description = """
Critère de tri.

On peut spécifier la direction de tri :
 - o=critère pour l’ordre croissant
 - o=-critère pour l’ordre décroissant
    """

    @extend_schema(
        parameters=[
            OpenApiParameter(name=CODE_INSEE_PARAM_NAME,
                             description="Filtre par code INSEE de la ville",
                             required=True,
                             type=str),
            OpenApiParameter(
                name=DISTANCE_FROM_CODE_INSEE_PARAM_NAME,
                description=
                f"Filtre par rayon de recherche autour de la ville, en kilomètres. Maximum {MAX_DISTANCE_RADIUS_KM} kilomètres",  # noqa: E501
                required=True,
                type=str,
            ),
            OpenApiParameter(name="format",
                             description="Format de sortie",
                             required=False,
                             enum=["json", "api"]),
            OpenApiParameter(
                name="o",
                description=sort_description,
                required=False,
                enum=SIAE_ORDERING_FILTER_MAPPING.values(),
            ),
        ],
        responses={
            200: SiaeSerializer,
            404: OpenApiTypes.OBJECT
        },
        examples=[
            NOT_FOUND_RESPONSE,
        ],
    )
    def list(self, request):
        # we need this despite the default behavior because of the documentation annotations
        return super().list(request)

    def get_queryset(self):
        # We only get to this point if permissions are OK
        queryset = Siae.objects

        # Get (registered) query parameters filters
        queryset = self._filter_by_query_params(self.request, queryset)

        try:
            return queryset.order_by("id")
        finally:
            # Tracking is currently done via user-agent header
            logger.info(
                "User-Agent: %s",
                self.request.headers.get("User-Agent"),
            )

    def _filter_by_query_params(self, request, queryset):
        params = request.query_params
        code_insee = params.get(CODE_INSEE_PARAM_NAME)
        t = f"Les paramètres `{CODE_INSEE_PARAM_NAME}` et `{DISTANCE_FROM_CODE_INSEE_PARAM_NAME}` sont obligatoires."
        if params.get(DISTANCE_FROM_CODE_INSEE_PARAM_NAME) and code_insee:
            distance_filter = int(
                params.get(DISTANCE_FROM_CODE_INSEE_PARAM_NAME))
            if distance_filter < 0 or distance_filter > MAX_DISTANCE_RADIUS_KM:
                raise ValidationError(
                    f"Le paramètre `{DISTANCE_FROM_CODE_INSEE_PARAM_NAME}` doit être compris entre 0 et {MAX_DISTANCE_RADIUS_KM}."  # noqa: E501
                )

            try:
                city = City.objects.get(code_insee=code_insee)
                return queryset.within(city.coords, distance_filter)
            except City.DoesNotExist:
                # Ensure the error comes from a missing city, which may not be that clear
                # with get_object_or_404
                raise NotFound(
                    f"Pas de ville avec pour code_insee {code_insee}")
        else:
            raise ValidationError(t)

        return queryset
Exemple #13
0
from drf_spectacular.utils import OpenApiParameter, OpenApiExample
from drf_spectacular.types import OpenApiTypes

# We keep a list of all parameters here,
# to make it easier to included them in the doc
backers_api_parameters = []

q_param = OpenApiParameter(
    name='q',
    type=OpenApiTypes.STR,
    location=OpenApiParameter.QUERY,
    description="Rechercher par nom."
    "<br /><br />"
    "Note : il est possible d'avoir des résultats pertinents avec seulement le début du nom.",
    examples=[
        OpenApiExample('', value=''),
        OpenApiExample('ademe', value='ademe'),
        OpenApiExample('conseil régional', value='conseil régional'),
        OpenApiExample('agenc', value='agenc')
    ])
backers_api_parameters.append(q_param)

has_financed_aids_param = OpenApiParameter(
    name='has_financed_aids',
    type=OpenApiTypes.BOOL,
    location=OpenApiParameter.QUERY,
    description="Renvoyer seulement les porteurs d'aides avec des aides.",
    examples=[
        OpenApiExample('', value=''),
        OpenApiExample('true', value=True)
    ])
Exemple #14
0
        class Fixed(self.target_class):
            @extend_schema(
                operation_id="get_user",
                description="Returns the user information for the currently logged on user.",
                responses={
                    status.HTTP_200_OK: UserSerializer,
                    status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                        DetailResponseSerializer,
                        description="Unauthorized",
                        examples=[
                            OpenApiExample(
                                "No token",
                                value={"detail": "Invalid token."},
                                status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                            )
                        ],
                    ),
                },
                tags=["users"],
            )
            def get(self, *args, **kwargs):
                pass

            @extend_schema(
                operation_id="update_user",
                description="Updates the user information for the currently logged on user.",
                request={"application/json": UserSerializer},
                responses={
                    status.HTTP_200_OK: UserSerializer,
                    status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                        DetailResponseSerializer,
                        description="Unauthorized",
                        examples=[
                            OpenApiExample(
                                "No token",
                                value={"detail": "Invalid token."},
                                status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                            )
                        ],
                    ),
                },
                tags=["users"],
            )
            def put(self, *args, **kwargs):
                pass

            @extend_schema(
                operation_id="partial_update_user",
                description="Partially updates the user information for the currently logged on user.",
                request={"application/json": UserSerializer},
                responses={
                    status.HTTP_200_OK: UserSerializer,
                    status.HTTP_401_UNAUTHORIZED: OpenApiResponse(
                        DetailResponseSerializer,
                        description="Unauthorized",
                        examples=[
                            OpenApiExample(
                                "No token",
                                value={"detail": "Invalid token."},
                                status_codes=[f"{status.HTTP_401_UNAUTHORIZED}"],
                            )
                        ],
                    ),
                },
                tags=["users"],
            )
            def patch(self, *args, **kwargs):
                pass
Exemple #15
0
class CharacterListAPIView(ListCreateAPIView):
    queryset = Character.objects.all()
    serializer_class = CharacterListSerializer

    @extend_schema(operation_id='Get list of all characters',
                   examples=[
                       OpenApiExample('Example 1',
                                      response_only=True,
                                      value=[{
                                          "id": 1,
                                          "strength_modifier": 4,
                                          "dexterity_modifier": 3,
                                          "intelligence_modifier": 2,
                                          "constitution_modifier": 0,
                                          "wisdom_modifier": -1,
                                          "charisma_modifier": -2,
                                          "strength_score": 18,
                                          "dexterity_score": 17,
                                          "intelligence_score": 15,
                                          "constitution_score": 10,
                                          "wisdom_score": 8,
                                          "charisma_score": 7
                                      }])
                   ])
    def get(self, request, *args, **kwargs):
        """
        Get list of all character's information. You could picture this as
        the list of all character sheets.
        """
        return super().get(request, *args, **kwargs)

    @extend_schema(operation_id='Create a character',
                   responses={201: CharacterListSerializer},
                   examples=[
                       OpenApiExample('Example 1',
                                      request_only=True,
                                      value={
                                          'strength_score': 18,
                                          'dexterity_score': 17,
                                          'intelligence_score': 15,
                                          'constitution_score': 10,
                                          'wisdom_score': 8,
                                          'charisma_score': 7
                                      }),
                       OpenApiExample('Example 2',
                                      response_only=True,
                                      value={
                                          "id": 1,
                                          "strength_modifier": 4,
                                          "dexterity_modifier": 3,
                                          "intelligence_modifier": 2,
                                          "constitution_modifier": 0,
                                          "wisdom_modifier": -1,
                                          "charisma_modifier": -2,
                                          "strength_score": 18,
                                          "dexterity_score": 17,
                                          "intelligence_score": 15,
                                          "constitution_score": 10,
                                          "wisdom_score": 8,
                                          "charisma_score": 7
                                      })
                   ])
    def post(self, request, *args, **kwargs):
        """
        Create a character information.
        """

        return super().post(request, *args, **kwargs)
Exemple #16
0
from dj_rest_auth.serializers import LoginSerializer as OriginalLoginSerializer
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiExample, extend_schema_serializer, extend_schema_field
from rest_framework import serializers
from rest_framework.reverse import reverse


@extend_schema_serializer(examples=[
    OpenApiExample('Local development example',
                   value={
                       'username': '******',
                       'password': '******',
                   },
                   request_only=True),
])
class LoginSerializer(OriginalLoginSerializer
                      ):  # remove third optional field from library
    """
    The API serializer for username / password login.
    """
    username = serializers.CharField()
    password = serializers.CharField()
    email = None


class JWTSerializer(serializers.Serializer):
    """
    The API serializer for getting security information after successful login.
    """
    access_token = serializers.CharField()
    access_token_verify_url = serializers.SerializerMethodField()
from rest_framework.response import Response

from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import (
    OpenApiExample,
    OpenApiParameter,
    extend_schema,
    extend_schema_serializer,
)
from tests import assert_schema, generate_schema


@extend_schema_serializer(examples=[
    OpenApiExample(
        'Serializer A Example RO',
        value={"field": 1},
        response_only=True,
    ),
    OpenApiExample(
        'Serializer A Example WO',
        value={"field": 2},
        request_only=True,
    ),
    OpenApiExample('Serializer A Example RW',
                   summary='Serializer A Example RW custom summary',
                   value={'field': 3}),
    OpenApiExample('Serializer A Example RW External',
                   external_value='https://example.com/example_a.txt',
                   media_type='application/x-www-form-urlencoded')
])
class ASerializer(serializers.Serializer):
Exemple #18
0
class OrganizationSCIMMemberDetails(SCIMEndpoint, OrganizationMemberEndpoint):
    permission_classes = (OrganizationSCIMMemberPermission, )
    public = {"GET", "DELETE", "PATCH"}

    def _delete_member(self, request: Request, organization, member):
        audit_data = member.get_audit_log_data()
        if member.is_only_owner():
            raise PermissionDenied(detail=ERR_ONLY_OWNER)
        with transaction.atomic():
            AuthIdentity.objects.filter(
                user=member.user,
                auth_provider__organization=organization).delete()
            member.delete()
            self.create_audit_entry(
                request=request,
                organization=organization,
                target_object=member.id,
                target_user=member.user,
                event=AuditLogEntryEvent.MEMBER_REMOVE,
                data=audit_data,
            )

    def _should_delete_member(self, operation):
        if operation["op"].lower() == MemberPatchOps.REPLACE:
            if isinstance(operation["value"],
                          dict) and operation["value"]["active"] is False:
                # how okta sets active to false
                return True
            elif operation["path"] == "active" and operation["value"] is False:
                # how other idps set active to false
                return True
        return False

    @extend_schema(
        operation_id="Query an Individual Organization Member",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIM_PARAMS.MEMBER_ID],
        request=None,
        responses={
            200: OrganizationMemberSCIMSerializer,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "Successful response",
                value={
                    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
                    "id":
                    "102",
                    "userName":
                    "******",
                    "emails": [{
                        "primary": True,
                        "value": "*****@*****.**",
                        "type": "work"
                    }],
                    "name": {
                        "familyName": "N/A",
                        "givenName": "N/A"
                    },
                    "active":
                    True,
                    "meta": {
                        "resourceType": "User"
                    },
                },
                status_codes=["200"],
            ),
        ],
    )
    def get(self, request: Request, organization, member) -> Response:
        """
        Query an individual organization member with a SCIM User GET Request.
        - The `name` object will contain fields `firstName` and `lastName` with the values of `N/A`.
        Sentry's SCIM API does not currently support these fields but returns them for compatibility purposes.
        """
        context = serialize(
            member,
            serializer=_scim_member_serializer_with_expansion(organization),
        )
        return Response(context)

    @extend_schema(
        operation_id="Update an Organization Member's Attributes",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIM_PARAMS.MEMBER_ID],
        request=SCIMPatchRequestSerializer,
        responses={
            204: RESPONSE_SUCCESS,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "Set member inactive",
                value={
                    "schemas":
                    ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
                    "Operations": [{
                        "op": "replace",
                        "value": {
                            "active": False
                        }
                    }],
                },
                status_codes=["204"],
            ),
        ],
    )
    def patch(self, request: Request, organization, member):
        """
        Update an organization member's attributes with a SCIM PATCH Request.
        The only supported attribute is `active`. After setting `active` to false
        Sentry will permanently delete the Organization Member.
        """

        serializer = SCIMPatchRequestSerializer(data=request.data)

        if not serializer.is_valid():
            return Response(
                {
                    "schemas": SCIM_API_ERROR,
                    "detail": json.dumps(serializer.errors)
                },
                status=400)

        result = serializer.validated_data

        for operation in result["operations"]:
            # we only support setting active to False which deletes the orgmember
            if self._should_delete_member(operation):
                self._delete_member(request, organization, member)
                return Response(status=204)
            else:
                return Response(SCIM_400_INVALID_PATCH, status=400)

        context = serialize(
            member,
            serializer=_scim_member_serializer_with_expansion(organization),
        )
        return Response(context)

    @extend_schema(
        operation_id="Delete an Organization Member via SCIM",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIM_PARAMS.MEMBER_ID],
        request=None,
        responses={
            204: RESPONSE_SUCCESS,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
    )
    def delete(self, request: Request, organization, member) -> Response:
        """
        Delete an organization member with a SCIM User DELETE Request.
        """
        self._delete_member(request, organization, member)
        return Response(status=204)
Exemple #19
0
                is_verified=True)
            return Response({"email": "Successfully activated"},
                            status=status.HTTP_200_OK)

        except jwt.exceptions.ExpiredSignatureError as err:
            return Response({"error": "Activation Expired"},
                            status=status.HTTP_400_BAD_REQUEST)

        except jwt.DecodeError as err:
            return Response({"error": "Invalid Token"},
                            status=status.HTTP_400_BAD_REQUEST)


@extend_schema(tags=["Register"],
               summary="login 하자",
               examples=[
                   OpenApiExample(request_only=True,
                                  name="sample1",
                                  value={
                                      "email": "*****@*****.**",
                                      "password": "******"
                                  }),
               ])
class LoginAPIView(generics.GenericAPIView):
    serializer_class = LoginSerializer

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
Exemple #20
0
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_view, extend_schema, OpenApiParameter, extend_schema_serializer, \
    OpenApiExample
from rest_framework import serializers, generics

from kubeportal.models.webapplication import WebApplication


@extend_schema_serializer(
    examples=[
        OpenApiExample(
            '',
            value={
                'link_name': 'Grafana',
                'link_url': 'https://monitoring.example.com',
                'category': 'MONITORING'
            },
            response_only=True
        ),
    ]
)
class WebAppSerializer(serializers.ModelSerializer):
    class Meta:
        model = WebApplication
        fields = ['link_name', 'link_url', 'category']

    def to_representation(self, data):
        data = super(WebAppSerializer, self).to_representation(data)
        link = data["link_url"]
        try:
            ns = self.context["request"].user.service_account.namespace.name
Exemple #21
0
class OrganizationStatsEndpointV2(OrganizationEventsEndpointBase):
    enforce_rate_limit = True
    rate_limits = {
        "GET": {
            RateLimitCategory.IP: RateLimit(20, 1),
            RateLimitCategory.USER: RateLimit(20, 1),
            RateLimitCategory.ORGANIZATION: RateLimit(20, 1),
        }
    }
    public = {"GET"}

    @extend_schema(
        operation_id="Retrieve Event Counts for an Organization (v2)",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, OrgStatsQueryParamsSerializer],
        request=None,
        responses={
            200:
            inline_sentry_response_serializer("Outcomes Response",
                                              StatsApiResponse),
            401:
            RESPONSE_UNAUTHORIZED,
            404:
            RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "Successful response",
                value={
                    "start":
                    "2022-02-14T19:00:00Z",
                    "end":
                    "2022-02-28T18:03:00Z",
                    "intervals": ["2022-02-28T00:00:00Z"],
                    "groups": [{
                        "by": {
                            "outcome": "invalid"
                        },
                        "totals": {
                            "sum(quantity)": 165665
                        },
                        "series": {
                            "sum(quantity)": [165665]
                        },
                    }],
                },
                status_codes=["200"],
            ),
        ],
    )
    def get(self, request: Request, organization) -> Response:
        """
        Query event counts for your Organization.
        Select a field, define a date range, and group or filter by columns.
        """
        with self.handle_query_errors():
            with sentry_sdk.start_span(op="outcomes.endpoint",
                                       description="build_outcomes_query"):
                query = self.build_outcomes_query(
                    request,
                    organization,
                )
            with sentry_sdk.start_span(op="outcomes.endpoint",
                                       description="run_outcomes_query"):
                result_totals = run_outcomes_query_totals(query)
                result_timeseries = (None
                                     if "project_id" in query.query_groupby
                                     else run_outcomes_query_timeseries(query))
            with sentry_sdk.start_span(op="outcomes.endpoint",
                                       description="massage_outcomes_result"):
                result = massage_outcomes_result(query, result_totals,
                                                 result_timeseries)
            return Response(result, status=200)

    def build_outcomes_query(self, request: Request, organization):
        params = {"organization_id": organization.id}
        project_ids = self._get_projects_for_orgstats_query(
            request, organization)

        if project_ids:
            params["project_id"] = project_ids

        return QueryDefinition(request.GET, params)

    def _get_projects_for_orgstats_query(self, request: Request, organization):
        # look at the raw project_id filter passed in, if its empty
        # and project_id is not in groupBy filter, treat it as an
        # org wide query and don't pass project_id in to QueryDefinition
        req_proj_ids = self.get_requested_project_ids_unchecked(request)
        if self._is_org_total_query(request, req_proj_ids):
            return None
        else:
            projects = self.get_projects(request,
                                         organization,
                                         project_ids=req_proj_ids)
            if not projects:
                raise NoProjects("No projects available")
            return [p.id for p in projects]

    def _is_org_total_query(self, request: Request, project_ids):
        return all([
            not project_ids or project_ids == ALL_ACCESS_PROJECTS,
            "project" not in request.GET.get("groupBy", []),
        ])

    @contextmanager
    def handle_query_errors(self):
        try:
            # TODO: this context manager should be decoupled from `OrganizationEventsEndpointBase`?
            with super().handle_query_errors():
                yield
        except (InvalidField, NoProjects, InvalidParams, InvalidQuery,
                InvalidParamsApi) as error:
            raise ParseError(detail=str(error))
Exemple #22
0

# We keep a list of all parameters here,
# to make it easier to included them in the doc
perimeters_api_parameters = []

q_param = OpenApiParameter(
    name='q',
    type=OpenApiTypes.STR,
    location=OpenApiParameter.QUERY,
    description="Rechercher par nom."
    "<br /><br />"
    "Note : il est possible d'avoir des résultats pertinents avec seulement le début du nom, \
    ou un nom légerement erroné.",
    examples=[
        OpenApiExample('', value=''),
        OpenApiExample('lyon', value='lyon'),
        OpenApiExample('par', value='par'),
        OpenApiExample('grenble', value='grenble')
    ])
perimeters_api_parameters.append(q_param)

scale_param = OpenApiParameter(
    name='scale',
    type=OpenApiTypes.STR,
    location=OpenApiParameter.QUERY,
    description="Filtrer par l'échelle."
    "<br /><br />"
    "Voir `/api/perimeters/scales/` pour la liste complète.",
    enum=[id for (weight, id, name) in Perimeter.SCALES_TUPLE],
    examples=[OpenApiExample('', value='')] +
Exemple #23
0
class OrganizationSCIMTeamIndex(SCIMEndpoint, OrganizationTeamsEndpoint):
    permission_classes = (OrganizationSCIMTeamPermission,)
    public = {"GET", "POST"}

    def team_serializer_for_post(self):
        return TeamSCIMSerializer(expand=["members"])

    def should_add_creator_to_team(self, request: Request):
        return False

    @extend_schema(
        operation_id="List an Organization's Paginated Teams",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIMQueryParamSerializer],
        request=None,
        responses={
            200: scim_response_envelope(
                "SCIMTeamIndexResponse", OrganizationTeamSCIMSerializerResponse
            ),
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "listGroups",
                value={
                    "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
                    "totalResults": 1,
                    "startIndex": 1,
                    "itemsPerPage": 1,
                    "Resources": [
                        {
                            "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
                            "id": "23232",
                            "displayName": "test-scimv2",
                            "members": [],
                            "meta": {"resourceType": "Group"},
                        }
                    ],
                },
                status_codes=["200"],
            ),
        ],
    )
    def get(self, request: Request, organization) -> Response:
        """
        Returns a paginated list of teams bound to a organization with a SCIM Groups GET Request.
        - Note that the members field will only contain up to 10000 members.
        """

        query_params = self.get_query_parameters(request)

        queryset = Team.objects.filter(
            organization=organization, status=TeamStatus.VISIBLE
        ).order_by("slug")
        if query_params["filter"]:
            queryset = queryset.filter(slug__iexact=slugify(query_params["filter"]))

        def data_fn(offset, limit):
            return list(queryset[offset : offset + limit])

        def on_results(results):
            results = serialize(
                results,
                None,
                TeamSCIMSerializer(expand=_team_expand(query_params["excluded_attributes"])),
            )
            return self.list_api_format(results, queryset.count(), query_params["start_index"])

        return self.paginate(
            request=request,
            on_results=on_results,
            paginator=GenericOffsetPaginator(data_fn=data_fn),
            default_per_page=query_params["count"],
            queryset=queryset,
            cursor_cls=SCIMCursor,
        )

    @extend_schema(
        operation_id="Provision a New Team",
        parameters=[GLOBAL_PARAMS.ORG_SLUG],
        request=inline_serializer(
            "SCIMTeamRequestBody",
            fields={
                "schemas": serializers.ListField(serializers.CharField()),
                "displayName": serializers.CharField(),
                "members": serializers.ListField(serializers.IntegerField()),
            },
        ),
        responses={
            201: TeamSCIMSerializer,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "provisionTeam",
                response_only=True,
                value={
                    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
                    "displayName": "Test SCIMv2",
                    "members": [],
                    "meta": {"resourceType": "Group"},
                    "id": "123",
                },
                status_codes=["201"],
            ),
        ],
    )
    def post(self, request: Request, organization) -> Response:
        """
        Create a new team bound to an organization via a SCIM Groups POST Request.
        Note that teams are always created with an empty member set.
        The endpoint will also do a normalization of uppercase / spaces to lowercase and dashes.
        """
        # shim displayName from SCIM api in order to work with
        # our regular team index POST
        request.data.update(
            {"name": request.data["displayName"], "slug": slugify(request.data["displayName"])}
        ),
        return super().post(request, organization)
Exemple #24
0
from django.utils.decorators import method_decorator
from drf_spectacular.extensions import OpenApiFilterExtension, OpenApiViewExtension
from drf_spectacular.openapi import AutoSchema
from drf_spectacular.plumbing import get_doc
from drf_spectacular.utils import OpenApiExample, extend_schema

from . import app_settings

wca_login_request_example = OpenApiExample(
    "WCA login request example",
    value={
        "code": "OTQ5HFpRcpwBJPxGZgwc0Dc5LpnTUXxVVNHxe2QDdEl",
        "callback_url": app_settings.WCA_DEFAULT_CALLBACK_URL,
    },
    request_only=True,
)
wca_login_response_example = OpenApiExample(
    "WCA login response example",
    value={"key": "cjcsu60bc8fi4hw9s4lbhgnbtc4ls1xlyga99qe0"},
    response_only=True,
)

user_retrieve_example = OpenApiExample(
    "User retrieve example",
    value={
        "first_name": "Juan",
        "last_name": "dela Cruz",
        "wca_id": "2021DELA01",
        "region": "NCR",
        "region_updated_at": "2021-04-24T03:14:50.069Z",
        "created_at": "2021-04-24T03:14:50.069Z",
Exemple #25
0
class OrganizationSCIMTeamDetails(SCIMEndpoint, TeamDetailsEndpoint):
    permission_classes = (OrganizationSCIMTeamPermission,)
    public = {"GET", "PATCH"}

    def convert_args(self, request: Request, organization_slug, team_id, *args, **kwargs):
        args, kwargs = super().convert_args(request, organization_slug)
        try:
            kwargs["team"] = self._get_team(kwargs["organization"], team_id)
        except Team.DoesNotExist:
            raise ResourceDoesNotExist(detail=SCIM_404_GROUP_RES)
        return (args, kwargs)

    def _get_team(self, organization, team_id):
        team = (
            Team.objects.filter(organization=organization, id=team_id)
            .select_related("organization")
            .get()
        )
        if team.status != TeamStatus.VISIBLE:
            raise Team.DoesNotExist
        return team

    @extend_schema(
        operation_id="Query an Individual Team",
        parameters=[SCIM_PARAMS.TEAM_ID, GLOBAL_PARAMS.ORG_SLUG],
        request=None,
        responses={
            200: TeamSCIMSerializer,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "Successful response",
                value={
                    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
                    "id": "23232",
                    "displayName": "test-scimv2",
                    "members": [],
                    "meta": {"resourceType": "Group"},
                },
            ),
        ],
    )
    def get(self, request: Request, organization, team) -> Response:
        """
        Query an individual team with a SCIM Group GET Request.
        - Note that the members field will only contain up to 10000 members.
        """
        query_params = self.get_query_parameters(request)

        context = serialize(
            team,
            serializer=TeamSCIMSerializer(expand=_team_expand(query_params["excluded_attributes"])),
        )
        return Response(context)

    def _add_members_operation(self, request: Request, operation, team):
        for member in operation["value"]:
            member = OrganizationMember.objects.get(
                organization=team.organization, id=member["value"]
            )
            if OrganizationMemberTeam.objects.filter(team=team, organizationmember=member).exists():
                # if a member already belongs to a team, do nothing
                continue

            with transaction.atomic():
                omt = OrganizationMemberTeam.objects.create(team=team, organizationmember=member)
                self.create_audit_entry(
                    request=request,
                    organization=team.organization,
                    target_object=omt.id,
                    target_user=member.user,
                    event=AuditLogEntryEvent.MEMBER_JOIN_TEAM,
                    data=omt.get_audit_log_data(),
                )

    def _remove_members_operation(self, request: Request, member_id, team):
        member = OrganizationMember.objects.get(organization=team.organization, id=member_id)
        with transaction.atomic():
            try:
                omt = OrganizationMemberTeam.objects.get(team=team, organizationmember=member)
            except OrganizationMemberTeam.DoesNotExist:
                return

            self.create_audit_entry(
                request=request,
                organization=team.organization,
                target_object=omt.id,
                target_user=member.user,
                event=AuditLogEntryEvent.MEMBER_LEAVE_TEAM,
                data=omt.get_audit_log_data(),
            )
            omt.delete()

    def _rename_team_operation(self, request: Request, new_name, team):
        serializer = TeamSerializer(
            team,
            data={"name": new_name, "slug": slugify(new_name)},
            partial=True,
        )
        if serializer.is_valid():
            team = serializer.save()
            self.create_audit_entry(
                request=request,
                organization=team.organization,
                target_object=team.id,
                event=AuditLogEntryEvent.TEAM_EDIT,
                data=team.get_audit_log_data(),
            )

    @extend_schema(
        operation_id="Update a Team's Attributes",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIM_PARAMS.TEAM_ID],
        request=SCIMTeamPatchRequestSerializer,
        responses={
            204: RESPONSE_SUCCESS,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
    )
    def patch(self, request: Request, organization, team):
        """
        A SCIM Group PATCH request takes a series of operations to perform on a team.
        It does them sequentially and if any of them fail no operations should go through.
        The operations are add members, remove members, replace members, and rename team.
        Update a team's attributes with a SCIM Group PATCH Request. Valid Operations are:
        * Renaming a team:
        ```json
        {
            "op": "replace",
            "value": {
                "id": 23,
                "displayName": "newName"
            }
        }
        ```
        * Adding a member to a team:
        ```json
        {
            "op": "add",
            "path": "members",
            "value": [
                {
                    "value": 23,
                    "display": "*****@*****.**"
                }
            ]
        }
        ```
        * Removing a member from a team:
        ```json
        {
            "op": "remove",
            "path": "members[value eq \"23\"]"
        }
        ```
        * Replacing an entire member set of a team:
        ```json
        {
            "op": "replace",
            "path": "members",
            "value": [
                {
                    "value": 23,
                    "display": "*****@*****.**"
                },
                {
                    "value": 24,
                    "display": "*****@*****.**"
                }
            ]
        }
        ```
        """
        operations = request.data.get("Operations", [])
        if len(operations) > 100:
            return Response(SCIM_400_TOO_MANY_PATCH_OPS_ERROR, status=400)
        try:
            with transaction.atomic():
                for operation in operations:
                    op = operation["op"].lower()
                    if op == TeamPatchOps.ADD and operation["path"] == "members":
                        self._add_members_operation(request, operation, team)
                    elif op == TeamPatchOps.REMOVE and "members" in operation["path"]:
                        self._remove_members_operation(
                            request, self._get_member_id_for_remove_op(operation), team
                        )
                    elif op == TeamPatchOps.REPLACE:
                        path = operation.get("path")

                        if path == "members":
                            # delete all the current team members
                            # and replace with the ones in the operation list
                            with transaction.atomic():
                                queryset = OrganizationMemberTeam.objects.filter(team_id=team.id)
                                queryset.delete()
                                self._add_members_operation(request, operation, team)
                        # azure and okta handle team name change operation differently
                        elif path is None:
                            # for okta
                            self._rename_team_operation(
                                request, operation["value"]["displayName"], team
                            )
                        elif path == "displayName":
                            # for azure
                            self._rename_team_operation(request, operation["value"], team)
                        else:
                            return Response(SCIM_400_UNSUPPORTED_ATTRIBUTE, status=400)

        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist(detail=SCIM_404_USER_RES)
        except IntegrityError as e:
            sentry_sdk.capture_exception(e)
            return Response(SCIM_400_INTEGRITY_ERROR, status=400)

        return self.respond(status=204)

    @extend_schema(
        operation_id="Delete an Individual Team",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIM_PARAMS.TEAM_ID],
        request=None,
        responses={
            204: RESPONSE_SUCCESS,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
    )
    def delete(self, request: Request, organization, team) -> Response:
        """
        Delete a team with a SCIM Group DELETE Request.
        """
        return super().delete(request, team)

    def put(self, request: Request, organization, team) -> Response:
        # override parent's put since we don't have puts
        # in SCIM Team routes
        return self.http_method_not_allowed(request)

    def _get_member_id_for_remove_op(self, operation):
        if "value" in operation:
            # azure sends member ids in this format under the key 'value'
            return operation["value"][0]["value"]

        try:
            # grab the filter out of the brackets of the string that looks
            # like so: members[value eq "123124"]
            regex_search = re.search(r"\[(.*?)\]", operation["path"])
            if regex_search is None:
                raise SCIMFilterError
            filter_path = regex_search.groups()[0]
            return parse_filter_conditions(filter_path)
        except SCIMFilterError:
            raise ParseError(detail=SCIM_400_INVALID_FILTER)
Exemple #26
0
    def get_override_parameters(self):
        """Expose the DSO-specific HTTP headers in all API methods."""
        extra = [
            OpenApiParameter(
                name=api_settings.URL_FORMAT_OVERRIDE,
                type={
                    "type":
                    "string",
                    "enum": [
                        renderer.format
                        for renderer in self.view.renderer_classes
                        if renderer.format != "api"  # Exclude browser view
                    ],
                },
                location=OpenApiParameter.QUERY,
                description="Select the export format",
                required=False,
            ),
        ]

        if isinstance(self.view, DSOViewMixin):
            extra += [
                OpenApiParameter(
                    "Accept-Crs",
                    type=OpenApiTypes.STR,
                    location=OpenApiParameter.HEADER,
                    description="Accept-Crs header for Geo queries",
                    required=False,
                ),
                OpenApiParameter(
                    "Content-Crs",
                    type=OpenApiTypes.STR,
                    location=OpenApiParameter.HEADER,
                    description="Content-Crs header for Geo queries",
                    required=False,
                ),
            ]

        # Expose expand parameters too.
        if issubclass(self.view.serializer_class, ExpandableSerializer):
            embeds = get_all_embedded_fields_by_name(
                self.view.serializer_class)
            examples = []
            if embeds:
                examples = [
                    OpenApiExample(
                        name=dotted_name,
                        value=dotted_name,
                        description=self._get_expand_description(field),
                    ) for dotted_name, field in sorted(embeds.items())
                ]
                examples.append(
                    OpenApiExample(
                        name="All Values",
                        value=",".join(sorted(embeds.keys())),
                        description=
                        "Expand all fields, identical to only using _expand=true.",
                    ))

            extra += [
                OpenApiParameter(
                    "_expand",
                    type=OpenApiTypes.BOOL,
                    location=OpenApiParameter.QUERY,
                    description="Allow to expand relations.",
                    required=False,
                ),
                OpenApiParameter(
                    "_expandScope",
                    type=OpenApiTypes.STR,
                    location=OpenApiParameter.QUERY,
                    description=
                    "Comma separated list of named relations to expand.",
                    required=False,
                    examples=examples,
                ),
            ]

        return extra
class ExampleTestWithExtendedViewSet(mixins.ListModelMixin,
                                     mixins.CreateModelMixin,
                                     viewsets.GenericViewSet):
    serializer_class = ASerializer

    @extend_schema(
        request=ASerializer,
        responses={
            201: BSerializer,
            400: OpenApiTypes.OBJECT,
            403: OpenApiTypes.OBJECT,
        },
        examples=[
            OpenApiExample(
                'Create Example RO',
                value={'field': 11},
                response_only=True,
            ),
            OpenApiExample(
                'Create Example WO',
                value={'field': 22},
                request_only=True,
            ),
            OpenApiExample(
                'Create Example RW',
                value={'field': 33},
            ),
            OpenApiExample('Create Error 403 Example',
                           value={'field': 'error'},
                           response_only=True,
                           status_codes=['403']),
        ],
    )
    def create(self, request, *args, **kwargs):
        super().create(request, *args, **kwargs)  # pragma: no cover

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name="artist",
                description="Filter by artist",
                required=False,
                type=str,
                examples=[
                    OpenApiExample(
                        "Artist Query Example 1",
                        value="prince",
                        description="description for artist query example 1"),
                    OpenApiExample(
                        "Artist Query Example 2",
                        value="miles davis",
                        description="description for artist query example 2")
                ]),
        ],
        responses=CSerializer,
    )
    def list(self, request):
        return Response()  # pragma: no cover

    @action(detail=False, methods=['GET'])
    def raw_action(self, request):
        return Response()  # pragma: no cover

    @extend_schema(responses=BSerializer)
    @action(detail=False, methods=['POST'])
    def override_extend_schema_action(self, request):
        return Response()  # pragma: no cover
Exemple #28
0
class OrganizationSCIMMemberIndex(SCIMEndpoint):
    permission_classes = (OrganizationSCIMMemberPermission, )
    public = {"GET", "POST"}

    @extend_schema(
        operation_id="List an Organization's Members",
        parameters=[GLOBAL_PARAMS.ORG_SLUG, SCIMQueryParamSerializer],
        request=None,
        responses={
            200:
            scim_response_envelope("SCIMMemberIndexResponse",
                                   OrganizationMemberSCIMSerializerResponse),
            401:
            RESPONSE_UNAUTHORIZED,
            403:
            RESPONSE_FORBIDDEN,
            404:
            RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "List an Organization's Members",
                value={
                    "schemas":
                    ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
                    "totalResults":
                    1,
                    "startIndex":
                    1,
                    "itemsPerPage":
                    1,
                    "Resources": [{
                        "schemas":
                        ["urn:ietf:params:scim:schemas:core:2.0:User"],
                        "id":
                        "102",
                        "userName":
                        "******",
                        "emails": [{
                            "primary": True,
                            "value": "*****@*****.**",
                            "type": "work"
                        }],
                        "name": {
                            "familyName": "N/A",
                            "givenName": "N/A"
                        },
                        "active":
                        True,
                        "meta": {
                            "resourceType": "User"
                        },
                    }],
                },
                status_codes=["200"],
            ),
        ],
    )
    def get(self, request: Request, organization) -> Response:
        """
        Returns a paginated list of members bound to a organization with a SCIM Users GET Request.
        """
        # note that SCIM doesn't care about changing results as they're queried

        query_params = self.get_query_parameters(request)

        queryset = (OrganizationMember.objects.filter(
            Q(invite_status=InviteStatus.APPROVED.value),
            Q(user__is_active=True) | Q(user__isnull=True),
            organization=organization,
        ).select_related("user").order_by("email", "user__email"))
        if query_params["filter"]:
            queryset = queryset.filter(
                Q(email__iexact=query_params["filter"])
                | Q(user__email__iexact=query_params["filter"])
            )  # not including secondary email vals (dups, etc.)

        def data_fn(offset, limit):
            return list(queryset[offset:offset + limit])

        def on_results(results):
            results = serialize(
                results,
                None,
                _scim_member_serializer_with_expansion(organization),
            )
            return self.list_api_format(results, queryset.count(),
                                        query_params["start_index"])

        return self.paginate(
            request=request,
            on_results=on_results,
            paginator=GenericOffsetPaginator(data_fn=data_fn),
            default_per_page=query_params["count"],
            queryset=queryset,
            cursor_cls=SCIMCursor,
        )

    @extend_schema(
        operation_id="Provision a New Organization Member",
        parameters=[GLOBAL_PARAMS.ORG_SLUG],
        request=inline_serializer(
            "SCIMMemberProvision",
            fields={"userName": serializers.EmailField()}),
        responses={
            201: OrganizationMemberSCIMSerializer,
            401: RESPONSE_UNAUTHORIZED,
            403: RESPONSE_FORBIDDEN,
            404: RESPONSE_NOTFOUND,
        },
        examples=[  # TODO: see if this can go on serializer object instead
            OpenApiExample(
                "Provision new member",
                response_only=True,
                value={
                    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
                    "id":
                    "242",
                    "userName":
                    "******",
                    "emails": [{
                        "primary": True,
                        "value": "*****@*****.**",
                        "type": "work"
                    }],
                    "active":
                    True,
                    "name": {
                        "familyName": "N/A",
                        "givenName": "N/A"
                    },
                    "meta": {
                        "resourceType": "User"
                    },
                },
                status_codes=["201"],
            ),
        ],
    )
    def post(self, request: Request, organization) -> Response:
        """
        Create a new Organization Member via a SCIM Users POST Request.
        - `userName` should be set to the SAML field used for email, and active should be set to `true`.
        - Sentry's SCIM API doesn't currently support setting users to inactive,
        and the member will be deleted if inactive is set to `false`.
        - The API also does not support setting secondary emails.
        """

        serializer = OrganizationMemberSerializer(
            data={
                "email": request.data.get("userName"),
                "role": roles.get(organization.default_role).id,
            },
            context={
                "organization": organization,
                "allowed_roles": [roles.get(organization.default_role)],
                "allow_existing_invite_request": True,
            },
        )

        if not serializer.is_valid():
            if "email" in serializer.errors and any(
                ("is already a member" in error)
                    for error in serializer.errors["email"]):
                # we include conflict logic in the serializer, check to see if that was
                # our error and if so, return a 409 so the scim IDP knows how to handle
                raise ConflictError(detail=SCIM_409_USER_EXISTS)
            return Response(serializer.errors, status=400)

        result = serializer.validated_data
        with transaction.atomic():
            member = OrganizationMember(
                organization=organization,
                email=result["email"],
                role=result["role"],
                inviter=request.user,
            )

            # TODO: are invite tokens needed for SAML orgs?
            if settings.SENTRY_ENABLE_INVITES:
                member.token = member.generate_token()
            member.save()

        self.create_audit_entry(
            request=request,
            organization_id=organization.id,
            target_object=member.id,
            data=member.get_audit_log_data(),
            event=AuditLogEntryEvent.MEMBER_INVITE if
            settings.SENTRY_ENABLE_INVITES else AuditLogEntryEvent.MEMBER_ADD,
        )

        if settings.SENTRY_ENABLE_INVITES and result.get("sendInvite"):
            member.send_invite_email()
            member_invited.send_robust(
                member=member,
                user=request.user,
                sender=self,
                referrer=request.data.get("referrer"),
            )

        context = serialize(
            member,
            serializer=_scim_member_serializer_with_expansion(organization),
        )
        return Response(context, status=201)
Exemple #29
0
from aids.constants import (
    AUDIENCES_ALL,
    AID_TYPE_CHOICES, FINANCIAL_AIDS, TECHNICAL_AIDS, OTHER_AIDS)


# We keep a list of all parameters here,
# to make it easier to included them in the doc
aids_api_parameters = []

text = OpenApiParameter(
    name='text',
    type=OpenApiTypes.STR,
    location=OpenApiParameter.QUERY,
    description="Recherche textuelle.",
    examples=[
        OpenApiExample('', value=''),
        OpenApiExample('velo', value='velo'),
        OpenApiExample('piste OU velo', value='piste+velo'),
        OpenApiExample('piste ET velo', value='piste velo')
    ])
aids_api_parameters.append(text)

targeted_audiences = OpenApiParameter(
    name='targeted_audiences',
    type=OpenApiTypes.STR,
    location=OpenApiParameter.QUERY,
    description="La structure pour laquelle vous recherchez des aides."
    "<br /><br />"
    "Voir aussi `/api/aids/audiences/` pour la liste complète.",
    enum=[id for (id, key) in AUDIENCES_ALL],
    examples=[OpenApiExample('', value='')] +