# -*- coding: utf-8 -*- #
# Copyright 2018 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helper functions for interacting with the binauthz API."""

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from apitools.base.py import list_pager
from googlecloudsdk.api_lib.util import apis

API_NAME = 'containeranalysis'

V1_ALPHA1 = 'v1alpha1'
V1_BETA1 = 'v1beta1'
DEFAULT_VERSION = V1_BETA1


class Client(object):
  """A client to access containeranalysis for binauthz purposes."""

  def __init__(self, api_version=None):
    """Creates a ContainerAnalysisClient.

    Args:
      api_version: The containeranalysis API version to use.
    """
    if api_version is None:
      api_version = DEFAULT_VERSION

    self.client = apis.GetClientInstance(API_NAME, api_version)
    self.messages = apis.GetMessagesModule(API_NAME, api_version)

  def YieldAttestations(self, note_ref, artifact_url=None):
    """Yields occurrences associated with given AA Note.

    Args:
      note_ref: The Note reference that will be queried for attached
        occurrences. (containeranalysis.projects.notes Resource)
      artifact_url: URL of the artifact for which to fetch occurrences.
        If None, then all occurrences attached to the AA Note are returned.

    Yields:
      Occurrences bound to `note_ref` with matching `artifact_url` (if passed).
    """
    ListNoteOccurrencesRequest = (  # pylint: disable=invalid-name
        self.messages.ContaineranalysisProjectsNotesOccurrencesListRequest)
    occurrence_iter = list_pager.YieldFromList(
        self.client.projects_notes_occurrences,
        request=ListNoteOccurrencesRequest(
            name=note_ref.RelativeName(),
            filter=(
                'resourceUrl="{}"'.format(artifact_url)
                if artifact_url is not None else ''),
        ),
        field='occurrences',
        batch_size=100,
        batch_size_attribute='pageSize',
    )

    # TODO(b/69380601): This should be handled by the filter parameter to
    # ListNoteOccurrences, but filtering isn't implemented yet for the fields
    # we care about.
    def MatchesFilter(occurrence):
      if (occurrence.kind !=
          self.messages.Occurrence.KindValueValuesEnum.ATTESTATION):
        return False
      return True

    for occurrence in occurrence_iter:
      if MatchesFilter(occurrence):
        yield occurrence

  def CreatePgpAttestationOccurrence(self, note_ref, project_ref, artifact_url,
                                     pgp_key_fingerprint, signature):
    """Creates Occurrence referencing given URL and Note.

    Args:
      note_ref: The Note reference that the created Occurrence will be
        bound to. (containeranalysis.projects.notes Resource)
      project_ref: The project ref where the Occurrence will be
        created. (cloudresourcemanager.projects Resource)
      artifact_url: URL of artifact to which the signature is associated
        (string)
      pgp_key_fingerprint: The ID of the public key that will be used to verify
        the signature.  This is usually generated by running
        `gpg --list-keys attesting_user@example.com` or equivalent.
        Note that this must match the public key inside the policy (managed
        separately) for the role being verified. (string)
      signature: The content artifact's signature, in the gpg
        clearsigned, ASCII-armored format.  Normally this is generated by
        running
        `gpg --local-user attesting_user@example.com --armor --sign <infile>`
        over the output of `CreateSignaturePayload`. (string)

    Returns:
      Created Occurrence.
    """
    attestation = self.messages.Attestation(
        pgpSignedAttestation=self.messages.PgpSignedAttestation(
            contentType=(
                self.messages.PgpSignedAttestation.ContentTypeValueValuesEnum.
                SIMPLE_SIGNING_JSON),
            signature=signature,
            pgpKeyId=pgp_key_fingerprint,
        ))
    occurrence = self.messages.Occurrence(
        kind=self.messages.Occurrence.KindValueValuesEnum.ATTESTATION,
        resource=self.messages.Resource(uri=artifact_url),
        noteName=note_ref.RelativeName(),
        attestation=self.messages.Details(attestation=attestation),
    )
    request = self.messages.ContaineranalysisProjectsOccurrencesCreateRequest(
        parent=project_ref.RelativeName(),
        occurrence=occurrence,
    )
    return self.client.projects_occurrences.Create(request)

  def CreateGenericAttestationOccurrence(self, note_ref, project_ref,
                                         artifact_url, public_key_id, signature,
                                         plaintext):
    """Creates Occurrence referencing given URL and Note.

    Args:
      note_ref: The Note reference that the created Occurrence will be
        bound to. (containeranalysis.projects.notes Resource)
      project_ref: The project ref where the Occurrence will be
        created. (cloudresourcemanager.projects Resource)
      artifact_url: URL of artifact to which the signature is associated.
        (string)
      public_key_id: The URI of the public key that will be used to verify the
        signature. (string)
      signature: The content artifact's signature as generated by the specified
        key's signing operation. (string)
      plaintext: The content that was signed. (string)

    Returns:
      Created Occurrence.
    """
    attestation = self.messages.Attestation(
        genericSignedAttestation=self.messages.GenericSignedAttestation(
            contentType=(self.messages.GenericSignedAttestation
                         .ContentTypeValueValuesEnum.SIMPLE_SIGNING_JSON),
            serializedPayload=plaintext,
            signatures=[
                self.messages.Signature(
                    publicKeyId=public_key_id, signature=signature),
            ],
        ))
    occurrence = self.messages.Occurrence(
        kind=self.messages.Occurrence.KindValueValuesEnum.ATTESTATION,
        resource=self.messages.Resource(uri=artifact_url),
        noteName=note_ref.RelativeName(),
        attestation=self.messages.Details(attestation=attestation),
    )
    request = self.messages.ContaineranalysisProjectsOccurrencesCreateRequest(
        parent=project_ref.RelativeName(),
        occurrence=occurrence,
    )
    return self.client.projects_occurrences.Create(request)
