def httpauth_login_required(func):
    """
        Put this decorator before your view to check if the user is logged in
        via httpauth and return a JSON 401 error if he/she is not.
    """

    def wrapper(request, *args, **kwargs):
        user = None
        # get the Basic username and password from the request.
        if 'HTTP_AUTHORIZATION' in request.META:
            auth = request.META['HTTP_AUTHORIZATION'].split()
            if len(auth) == 2:
                if auth[0].lower() == "basic":
                    username, password = base64.b64decode(
                        auth[1]).decode('utf-8').split(':')
                    # print(username, password)
                    user = authenticate(username=username, password=password)

        if not user or not user.is_active:
            return HttpResponse(unauthorized_json_response(),
                                content_type="application/json", status=401)
        login(request, user)
        return func(request, *args, **kwargs)

    return update_wrapper(wrapper, func) def ip_write_verification_required(func):
    """
        Put this decorator before your view to check if the function is coming from an IP on file
    """

    def wrapper(request, *args, **kwargs):

        slug = kwargs.get('slug', "")
        if not slug:
            return kickout_404("Not found.", content_type="application/json")

        try:
            wip = WriteAPIIP.objects.get(slug=slug)
            ip = get_client_ip(request)
            if ip not in wip.allowable_ips() and "0.0.0.0" not in wip.allowable_ips():
                msg = "The IP %s is not authorized to make the API call." % (
                    ip)
                return kickout_401(msg)

        except WriteAPIIP.DoesNotExist:
            return HttpResponse(unauthorized_json_response(),
                                content_type="application/json")

        return func(request, *args, **kwargs)

    return update_wrapper(wrapper, func) def kickout_400(reason, status_code=400):
    response = OrderedDict()
    response["code"] = status_code
    response["status"] = "Client error"
    response["errors"] = [reason, ]
    return HttpResponse(json.dumps(response, indent=4),
                        content_type="application/json") def kickout_404(reason, status_code=404):
    response = OrderedDict()
    response["code"] = status_code
    response["status"] = "NOT FOUND"
    response["errors"] = [reason, ]
    return HttpResponse(json.dumps(response, indent=4),
                        content_type="application/json") def kickout_500(reason, status_code=500):
    response = OrderedDict()
    response["code"] = status_code
    response["status"] = "SERVER SIDE ERROR"
    response["errors"] = [reason, ]
    return HttpResponse(json.dumps(response, indent=4),
                        content_type="application/json") def write_mongo(document, database_name,
                collection_name, update=False):
    """Write a document to the collection. Return a response_dict containing
    the written record. Method functions as both insert or update based on update
    parameter"""

    l = []
    response_dict = {}
    try:
        mongodb_client_url = getattr(settings, 'MONGODB_CLIENT',
                                     'mongodb://localhost:27017/')
        mc = MongoClient(mongodb_client_url, document_class=OrderedDict)
        db = mc[str(database_name)]
        collection = db[str(collection_name)]

        # Cast the query to integers
        # if settings.CAST_ININGS_TO_INTEGERS:
        #    query = cast_number_strings_to_integers(query)

        potential_key_found = False
        existing_transaction_id = None
        existing_mongo_id = None

        # enforce non-repudiation constraint on create
        # if document.has_key("transaction_id"):
        #    existing_transaction_id = collection.find_one({'transaction_id':document['transaction_id']})
        #    if existing_transaction_id:
        #        potential_key_found = True

        if "id" in document:
            document["_id"] = ObjectId(document["id"])
            del document["id"]

        if "_id" in document:
            existing_mongo_id = collection.find_one({'_id': document['_id']})
            if existing_mongo_id:
                potential_key_found = True

        if update == False and potential_key_found == True:
            """409 conflict"""
            response_dict['code'] = 409
            response_dict['type'] = "Error"
            response_dict['results'] = []
            response_dict[
                'message'] = "Perhaps you meant to perform an update instead?"
            response_dict['errors'] = [
                "Conflict. This transaction_id has already been created.", ]
            return response_dict

        elif update and potential_key_found:  # this is an update
            # set kwargs _id to the existing_id to force to overwrite existing
            # document

            # if existing_transaction_id:
            #
            #    document['_id'] = ObjectId(existing_transaction_id['_id'])
            #    document['history']=True
            #    history_collection_name = "%s_history" % str(collection_name)
            #    history_collection   = db[str(history_collection_name)]
            #
            #    history_object = existing_transaction_id
            #    history_object['historical_id'] = existing_transaction_id['_id']
            #    del history_object['_id']
            #    #now write the record to the historical collection
            #    written_object = history_collection.insert(history_object)

            if existing_mongo_id:
                document['_id'] = ObjectId(existing_mongo_id['_id'])
                document['history'] = True
                history_collection_name = "%s_history" % str(collection_name)
                history_collection = db[str(history_collection_name)]

                # print history_collection
                # print existing_mongo_id

                history_object = existing_mongo_id

                history_object['historical_id'] = existing_mongo_id['_id']
                del history_object['_id']
                # print history_object

                # now write the record to the historical collection
                written_object = history_collection.insert(history_object)

            # update the record
            myobjectid = collection.save(document)

        else:
            # this is new so perform an insert.
            myobjectid = collection.insert(document)

        # now fetch the record we just wrote so that we write it back to the
        # DB.
        myobject = collection.find_one({'_id': myobjectid})
        response_dict['code'] = 200
        response_dict['type'] = "write-results"
        myobject['id'] = myobject['_id'].__str__()
        del myobject['_id']
        l.append(myobject)
        response_dict['results'] = l

    except Exception:
        # print "Error reading from Mongo"
        # print str(sys.exc_info())
        response_dict['code'] = 400
        response_dict['type'] = "Error"
        response_dict['results'] = []
        response_dict['message'] = str(sys.exc_info())
    return response_dict def checkObjectId(s):
    try:
        ObjectId(s)
    except InvalidId:
        return False
    return True class WriteAPIHTTPAuth(models.Model):
    created_by = models.ForeignKey(
        get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
    groups = models.ManyToManyField(
        Group,
        blank=True,
        related_name="djmongo_write_httpauth_groups",
        help_text=_(
            "Use ctrl to select multiple "
            "groups. If no groups are "
            "selected blank, any user may "
            "access the API."))
    slug = models.SlugField(max_length=100, unique=True, help_text=_(
        "The slug is the unique part of the URL for your API."))

    http_post = models.BooleanField(default=True, blank=True)
    http_put = models.BooleanField(default=True, blank=True)
    database_name = models.CharField(max_length=100)
    collection_name = models.CharField(max_length=100)
    json_schema = models.TextField(
        max_length=20480,
        default="{}",
        verbose_name="JSON Schema",
        help_text="""Default "{}", means no JSON Schema.""")
    readme_md = models.TextField(max_length=4096, default="", blank=True)
    creation_date = models.DateField(auto_now_add=True)

    class Meta:
        get_latest_by = "creation_date"
        ordering = ('-creation_date',)
        verbose_name_plural = _("Write APIs with HTTPAuth")
        verbose_name = _("Write API with HTTPAuth")

    def url(self):
        return reverse('djmongo_api_write_to_collection_with_httpauth', args=(self.slug,))

    def __str__(self):
        return "%s" % (self.slug)

    def allgroups(self):
        groups = []
        for g in self.groups.all():
            groups.append(g.name)
        return json.dumps(groups)

    def http_methods(self):
        l = []
        if self.http_post:
            l.append("POST")
        if self.http_put:
            l.append("PUT")
        return l

    def auth_method(self):
        return 'httpauth'

    def http_get_response(self):
        od = OrderedDict()
        od['http_methods'] = self.http_methods()
        od['auth_method'] = self.auth_method()
        od['json_schema'] = self.json_schema
        od['readme'] = self.readme_md
        return od class WriteAPIIP(models.Model):
    created_by = models.ForeignKey(
        get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
    slug = models.SlugField(max_length=100, unique=True)
    http_post = models.BooleanField(default=True, blank=True)
    http_put = models.BooleanField(default=True, blank=True)
    database_name = models.CharField(max_length=100)
    collection_name = models.CharField(max_length=100)
    json_schema = models.TextField(
        max_length=20480,
        default="{}",
        verbose_name="JSON Schema",
        help_text="""Default "{}", means no JSON Schema.""")
    from_ip = models.TextField(max_length=2048, default="127.0.0.1",
                               verbose_name=_("From IPs"),
                               help_text=_("Only accept requests from a IP in "
                                           "this list separated by whitespace "
                                           ". 0.0.0.0 means all."))
    readme_md = models.TextField(max_length=4096, default="", blank=True)
    creation_date = models.DateField(auto_now_add=True)

    class Meta:
        get_latest_by = "creation_date"
        ordering = ('-creation_date',)
        verbose_name_plural = _("Write APIs with IP Address Auth")
        verbose_name = _("Write API with IP Address Auth")

    def allowable_ips(self):
        allowable_ips = self.from_ip.split(" ")
        return allowable_ips

    def __str__(self):
        return "%s" % (self.slug)

    def url(self):
        return reverse('djmongo_api_write_to_collection_with_ip', args=(self.slug,))

    def http_methods(self):
        l = []
        if self.http_post:
            l.append("POST")
        if self.http_put:
            l.append("PUT")
        return l

    def auth_method(self):
        return 'ip'

    def http_get_response(self):
        od = OrderedDict()
        od['http_methods'] = self.http_methods()
        od['auth_method'] = self.auth_method()
        od['json_schema'] = self.json_schema
        od['readme'] = self.readme_md
        return od class WriteAPIOAuth2(models.Model):

    created_by = models.ForeignKey(
        get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
    scopes = models.CharField(max_length=1024, default="*", blank=True,
                              help_text="Space delimited list of scopes required. * means no scope is required.")
    http_post = models.BooleanField(default=True, blank=True)
    http_put = models.BooleanField(default=True, blank=True)
    slug = models.SlugField(max_length=100, unique=True)
    database_name = models.CharField(max_length=100)
    collection_name = models.CharField(max_length=100)
    json_schema = models.TextField(
        max_length=20480,
        default="{}",
        verbose_name="JSON Schema",
        help_text="""Default "{}", means no JSON Schema.""")
    readme_md = models.TextField(max_length=4096, default="", blank=True)
    creation_date = models.DateField(auto_now_add=True)

    class Meta:
        get_latest_by = "creation_date"
        ordering = ('-creation_date',)
        verbose_name_plural = "Write APIs with OAuth2 Auth"
        verbose_name = "Write API with OAuth2 Auth"

    def __str__(self):
        return "%s" % (self.slug)

    def http_methods(self):
        l = []
        if self.http_post:
            l.append("POST")
        if self.http_put:
            l.append("PUT")
        return l

    def auth_method(self):
        return 'oauth2'

    def http_get_response(self):
        od = OrderedDict()
        od['http_methods'] = self.http_methods()
        od['auth_method'] = self.auth_method()
        od['json_schema'] = self.json_schema
        od['readme'] = self.readme_md
        return od class WriteAPIHTTPAuthForm(forms.ModelForm):

    class Meta:
        model = WriteAPIHTTPAuth
        fields = (
            'slug',
            'http_post',
            'http_put',
            'database_name',
            'collection_name',
            'readme_md',
            'groups',
            'json_schema')
    required_css_class = 'required'

    def clean_json_schema(self):
        json_schema = self.cleaned_data["json_schema"]
        try:
            json.loads(json_schema, object_pairs_hook=OrderedDict)
        except ValueError:
            raise forms.ValidationError(_('The JSONSchema was invalid JSON'))
        return json_schema class WriteAPIHTTPAuthDeleteForm(forms.Form):

    slug = forms.CharField(label=_("Slug"),
                           help_text=_("Retype the slug to confirm deletion."))

    def clean_slug(self):
        slug = self.cleaned_data["slug"]
        try:
            WriteAPIHTTPAuth.objects.get(slug=slug)
        except WriteAPIHTTPAuth.DoesNotExist:
            raise forms.ValidationError(_('There is no API with that slug.'))
        return slug class WriteAPIIPDeleteForm(forms.Form):

    slug = forms.CharField(label=_("Slug"),
                           help_text=_("Retype the slug to confirm deletion."))

    def clean_slug(self):
        slug = self.cleaned_data["slug"]
        try:
            WriteAPIIP.objects.get(slug=slug)
        except WriteAPIIP.DoesNotExist:
            raise forms.ValidationError(_('There is no API with that slug.'))
        return slug class WriteAPIIPForm(forms.ModelForm):

    class Meta:
        model = WriteAPIIP
        fields = (
            'slug',
            'http_post',
            'http_put',
            'database_name',
            'collection_name',
            'from_ip',
            'readme_md',
            'json_schema')
    required_css_class = 'required'

    def clean_json_schema(self):
        json_schema = self.cleaned_data["json_schema"]
        try:
            json_schema_load = json.loads(
                json_schema, object_pairs_hook=OrderedDict)
        except ValueError:
            raise forms.ValidationError(_('The JSONSchema was invalid JSON'))
        return json_schema class WriteAPIOAuth2Form(forms.ModelForm):

    class Meta:
        model = WriteAPIOAuth2
        fields = (
            'slug',
            'http_post',
            'http_put',
            'database_name',
            'collection_name',
            'scopes',
            'readme_md',
            'json_schema')
    required_css_class = 'required'

    def clean_json_schema(self):
        json_schema = self.cleaned_data["json_schema"]
        try:
            json.loads(json_schema, object_pairs_hook=OrderedDict)
        except ValueError:
            raise forms.ValidationError(_('The JSON Schema was invalid JSON.'))
        return json_schema class WriteAPIOAuth2DeleteForm(forms.Form):

    slug = forms.CharField(label=_("Slug"),
                           help_text=_("Retype the slug to confirm deletion."))

    def clean_slug(self):
        slug = self.cleaned_data["slug"]
        try:
            WriteAPIOAuth2.objects.get(slug=slug)
        except WriteAPIOAuth2.DoesNotExist:
            raise forms.ValidationError(_('There is no API with that slug.'))
        return slug
from django.shortcuts import render, get_object_or_404
from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt
from ..decorators import (httpauth_login_required, ip_write_verification_required,
                          kickout_400, kickout_404, kickout_500)
from django.http import HttpResponse, HttpResponseRedirect
from collections import OrderedDict
from ..mongoutils import write_mongo, checkObjectId
from jsonschema import validate
from django.urls import reverse
from jsonschema.exceptions import ValidationError
from .models import WriteAPIHTTPAuth, WriteAPIIP, WriteAPIOAuth2
from .forms import (WriteAPIHTTPAuthForm, WriteAPIHTTPAuthDeleteForm,
                    WriteAPIIPDeleteForm, WriteAPIIPForm)
from .forms import WriteAPIOAuth2Form, WriteAPIOAuth2DeleteForm
from django.utils.translation import ugettext_lazy as _
import json
import sys
    # this is a GET
    context = {'name': name,
               'form': WriteAPIHTTPAuthForm(instance=a)
               }
    return render(
        request,
        'djmongo/console/generic/bootstrapform.html',
        context,
    )


def edit_ip_write_api(request, slug):
    a = get_object_or_404(WriteAPIIP, slug=slug)
    name = _("Edit IP-based Write API")
    if request.method == 'POST':
        form = WriteAPIIPForm(request.POST, instance=a)
        if form.is_valid():
            a = form.save()
            msg = _('The IP-based API for %s was updated.') % (slug)
            messages.success(request, msg)
            return HttpResponseRedirect(
                reverse('djmongo_show_apis',
                        args=(a.database_name,
                              a.collection_name)))
        else:
            # The form is invalid
            messages.error(
                request, _("Please correct the errors in the form."))
            context = {'form': form, 'name': name}
            return render(
                request,
                'djmongo/console/generic/bootstrapform.html',
                context)

    # this is a GET
    context = {'name': name,
               'form': WriteAPIIPForm(instance=a)
               }
    return render(
        request,
        'djmongo/console/generic/bootstrapform.html',
        context,
    )


def delete_httpauth_write_api(request, database_name, collection_name, slug):
    name = _("Delete HTTP Auth Write API")
    if request.method == 'POST':
        form = WriteAPIHTTPAuthDeleteForm(request.POST)
        if form.is_valid():
            a = WriteAPIHTTPAuth.objects.get(database_name=database_name,
                          collection_name=collection_name, slug=slug)
            a.delete()
            msg = _('The HTTP Auth API for %s was deleted.') % (slug)
            messages.success(request, msg)
            return HttpResponseRedirect(
                reverse('djmongo_show_apis',
                        args=(a.database_name,
                              a.collection_name)))
        else:
            # The form is invalid
            messages.error(
                request, _("Please correct the errors in the form."))
            context = {'form': form, 'name': name}
            return render(
                request,
                'djmongo/console/generic/bootstrapform.html',
                context)

    # this is a GET
    context = {'name': name,
               'form': WriteAPIHTTPAuthDeleteForm()
               }
    return render(
        request,
        'djmongo/console/generic/bootstrapform.html',
        context,
    )


def delete_ip_write_api(request, slug):
    name = _("Delete IP-based Write API")
    if request.method == 'POST':
        form = WriteAPIIPDeleteForm(request.POST)
        if form.is_valid():
            a = WriteAPIIP.objects.get(slug=slug)
            a.delete()
            msg = _('The IP-based API for %s was deleted.') % (slug)
            messages.success(request, msg)
            return HttpResponseRedirect(
                reverse('djmongo_show_apis',
                        args=(a.database_name,
                              a.collection_name)))
        else:
            # The form is invalid
            messages.error(
                request, _("Please correct the errors in the form."))
            context = {'form': form, 'name': name}
            return render(
                request,
                'djmongo/console/generic/bootstrapform.html',
                context)

    # this is a GET
    context = {'name': name,
               'form': WriteAPIIPDeleteForm()
               }
    return render(
        request,
        'djmongo/console/generic/bootstrapform.html',
        context,
    )


def create_oauth2_write_api(
        request,
        database_name=None,
        collection_name=None):
    name = _("Create an OAuth2 Write API")
    if request.method == 'POST':

        form = WriteAPIOAuth2Form(request.POST)