#!/usr/bin/env python
# -*- coding: utf8 -*-

description = """
Обертка над yav

Получить oauth токен: https://oauth.yandex-team.ru/authorize?response_type=token&client_id=ce68fbebc76c4ffda974049083729982

export yav_OAUTH=A...h

dt-yav ls 

создать новый секрет (!создастся даже если такой уже есть, имя -- неуникальный ключ)
dt-yav touch direct.test.tvm2_direct-intapi-test

dt-yav cat sec-01crqqbkhqxbjkjma72r0bs7sm:
dt-yav cat sec-01crqqbkhqxbjkjma72r0bs7sm:tvm2_direct-intapi-sandbox-test

cat file | dt-yav tee sec-01crqvkdqxptvjh2v247herxrm:tvm2_direct-intapi-test

Посмотреть права:
dt-yav stat sec-01crqpskg0pcnepv1p40xycv44

Добавить права:
группе:    
dt-yav chmod staff:42702+OWNER sec-01cp5pkfqct8e18nyydsb86gkc
человеку:
dt-yav chmod hmepas+READER sec-01cp5pkfqct8e18nyydsb86gkc

"""

TODO = """
- сделать проверку по имени перед touch
- в ls показывать время создания/модификации
- добавление роли по имени группы
- удаление роли
"""


import os
import re
import sys

sys.path.insert(0, '/opt/direct-py/startrek-python-client-sni-fix')

import argparse
import copy
import fnmatch
import hashlib
import json
import requests
import shutil
import socket
import subprocess
import tempfile
import time
import urllib
from collections import defaultdict

from requests.packages.urllib3.exceptions import InsecureRequestWarning
# чтобы не было ворнингов при обращении к tabula/yamb_send
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def init_cmds():
    global cmds
    cmds = {
            'cat': {
                'code': cmd_cat,
                'desc': 'вывести содержимое указанного секрета/ключа',
                },
            'tee': {
                'code': cmd_tee,
                'desc': 'прочитать stdin и записать в указанный секрет/ключ',
                },
            'touch': {
                'code': cmd_touch,
                'desc': 'создать секрет',
                },
            'ls': {
                'code': cmd_ls,
                'desc': 'список секретов',
                },
            'stat': {
                'code': cmd_stat,
                'desc': 'подробная информация о секрете, включая права',
                },
            'chmod': {
                'code': cmd_chmod,
                'desc': 'редактирование ролей (прав) на секрете',
                },
            }

#######
# Вспомогательные ф-ции
def die(message=''):
    sys.stderr.write("%s\n" % message)
    exit(1)

def my_system(cmd, verbose=False):
    if verbose:
        print("going to exec: %s\n" % cmd)
    exit_code = subprocess.call(cmd)
    if exit_code != 0:
        die("cmd failed, stop (%s)" % cmd)
    return
#######


#######
# Обработка команд

def cmd_cat(extra):
    for s in extra:
        m = re.match(r'(sec-\w+):(.*)', s)
        if not m:
            die("can't parse secret uuid and key")
        secret_uuid = m.group(1)
        secret_key = m.group(2)
        
        url_metadata = "https://vault-api.passport.yandex.net/1/secrets/%s/" % urllib.quote(secret_uuid)
        r = requests.get(url_metadata, headers = get_headers(), verify=False, timeout = 10)
        data = r.json()
        versions = sorted(data['secret']["secret_versions"], key = lambda v: v["created_at"], reverse=True)
        last_version = versions[0]["version"]
        #print "%s" % versions
        #url_version = "https://vault-api.passport.yandex.net/1/versions/ver-0000000000000000000000ygj2/" -H "Content-Type: application/json" -H "X-Ya-User-Ticket: <user_ticket>" -d '{}'
        url_version = "https://vault-api.passport.yandex.net/1/versions/%s/" % urllib.quote(last_version)
        r = requests.get(url_version, headers = get_headers(content_type=True), verify=False, timeout = 10)
        data = r.json()
        for k in data['version']['value']:
            if secret_key and secret_key != k['key']:
                continue
            if not secret_key: 
                print "### %s\n%s" % (k['key'], k['value'])
            else:
                print k['value']
    return 0



def cmd_tee(extra):
    if len(extra) != 1:
        die("expecting exactly 1 parameter, %s found" % (len(extra)))
    s = extra[0]
    m = re.match(r'(sec-\w+):(.*)', s)
    if not m:
        die("can't parse secret uuid and key")
    secret_uuid = m.group(1)
    secret_key = m.group(2)
    
    text = sys.stdin.read()

    url_metadata = "https://vault-api.passport.yandex.net/1/secrets/%s/" % urllib.quote(secret_uuid)
    r = requests.get(url_metadata, headers = get_headers(), verify=False, timeout = 10)
    data = r.json()
    versions = sorted(data['secret']["secret_versions"], key = lambda v: v["created_at"], reverse=True)
    if len(versions) == 0:
        #curl -X POST "https://vault-api.passport.yandex.net/1/secrets/sec-0000000000000000000000ygj0/versions/" -H "Content-Type: application/json" -H "X-Ya-User-Ticket: <user_ticket>" -d '{"value": [{"value": "ppodolsky", "key": "login"}, {"value": "123456", "key": "password"}]}'
        url_version_create = "https://vault-api.passport.yandex.net/1/secrets/%s/versions/" % urllib.quote(secret_uuid)
        data = {
                "value":[
                    {
                        'key': secret_key,
                        'value': text,
                        },
                    ],
                }
        print json.dumps(data)
        r = requests.post(url_version_create, data = json.dumps(data), headers = get_headers(content_type=True), verify=False, timeout = 10)
        print json.dumps(r.json())
    else:
        last_version = versions[0]["version"]
        #curl -X POST "https://vault-api.passport.yandex.net/1/versions/<string:parent_version_uuid>/" -H "Content-Type: application/json" -H "X-Ya-User-Ticket: <user_ticket>" -d '{"comment": "new version", "diff": [{"key": "login"}, {"value": "123456", "key": "password"}]}'
        url_update = "https://vault-api.passport.yandex.net/1/versions/%s/" % urllib.quote(last_version)
        data = {
                'diff': [
                    {
                        'key': secret_key,
                        'value': text,
                        },
                    ],
                }
        r = requests.post(url_update, data = json.dumps(data), headers = get_headers(content_type=True), verify=False, timeout = 10)
        print json.dumps(r.json())
        
    #sec-01crqvkdqxptvjh2v247herxrm
    return 0


def cmd_touch(extra):
    for s in extra:
        #curl -X POST "https://vault-api.passport.yandex.net/1/secrets/" -H "X-Ya-User-Ticket: <user_ticket>" -d 'name=passport_top_secret_1&comment=Dont%2Ftouch&tags=yp,search'
        url_create = "https://vault-api.passport.yandex.net/1/secrets/"
        data = {'name': s,
                }
        r = requests.post(url_create, data = data, headers = get_headers(), verify=False, timeout = 10)
        print json.dumps(r.json()) # чтобы печатать валидный json (r.text выводит еще и пустую строку в конце)
    return 0

def cmd_ls(extra):
    url_secrets = "https://vault-api.passport.yandex.net/1/secrets/"
    r = requests.get(url_secrets, headers = get_headers(), verify=False, timeout = 10)
    data = r.json()
    #print data
    for s in data['secrets']:
        #uuid': u'sec-01crqvkdqxptvjh2v247herxrm', u'effective_role': u'OWNER', u'name': u'direct.test.tvm2_direct-intapi-test'
        print "%s %s %s" % (s['uuid'], s['creator_login'], s['name'])
    return 0


def cmd_stat(extra):
    for s in extra:
        url_metadata = "https://vault-api.passport.yandex.net/1/secrets/%s/" % urllib.quote(s)
        r = requests.get(url_metadata, headers = get_headers(), verify=False, timeout = 10)
        data = r.json()
        print "### %s / %s" % (data['secret']['uuid'], data['secret']['name'])
        print "created: %s\nupdated: %s" % (data['secret']['created_at'], data['secret']['updated_at'])
        print "roles:"
        for role in data['secret']['secret_roles']: 
            subject = '~~~'
            if 'login' in role:
                subject = role['login']
            elif 'staff_name' in role:
                subject = "staff:%s / %s / %s" % (role['staff_id'], role['staff_slug'], role['staff_name'])
            print "  %s %s" % (role['role_slug'], subject)

    return 0


def cmd_chmod(extra):
    if len(extra) < 2:
        die("expecting at least 2 parameters")
    action = extra.pop(0)
    m = re.match(r'^(staff:\w+|[\w\-]+)([\+\-])(OWNER|READER)$', action)
    if not m:
        die("can't parse acl")
    subject_str = m.group(1)
    to_do = m.group(2)
    role = m.group(3)
    m = REMatcher()
    if m.match(r'^staff:([0-9]+)$', subject_str):
        subject = {
                'staff_id': m.group(1)
                }
    elif m.match(r'staff:(\w+)^$', subject_str):
        die("not implemented")
        # TODO превращать название подразделения в id
    elif m.match(r'^([a-z0-9\-]+)$', subject_str):
        subject = {
                'login': m.group(1)
                }
    else:
        die("can't process specifcation '%s'" % subject_str)

    url_role_add_tmpl = "https://vault-api.passport.yandex.net/1/secrets/%s/roles/"
    for s in extra: 
        #curl -X POST "https://vault-api.passport.yandex.net/1/secrets/0000000000000000000000ygj0/roles/" -H "X-Ya-User-Ticket: <user_ticket>" -d 'role=READER&staff_id=31782'
        url_role_add = url_role_add_tmpl % s
        data = subject.copy()
        data['role'] = role
        if to_do == '+': 
            r = requests.post(url_role_add, data = data, headers = get_headers(), verify=False, timeout = 10)
            print json.dumps(r.json())
        elif to_do == '-':
            die("not implemented")
        else:
            die("can't process action '%s'" % to_do)

    return 0

######
# вспомогательное
def get_headers(content_type=False):
    global token

    headers = { 
            "Authorization": "OAuth %s" % token,
            }
    if content_type:
        headers['Content-Type'] = 'application/json'
    return headers

class REMatcher(object):
    '''
    Проверяет соответствие регвыру И сохраняет группы совпадений
    '''
    def __init__(self):
        return

    def match(self, regexp, string):
        self.rematch = re.match(regexp, string)
        return bool(self.rematch)

    def group(self,i):
        return self.rematch.group(i)


######
# общий цикл 

def do_one_cmd(opts):

    # выполняем запрошенное действие
    cmds[opts.cmd]['code'](opts.extra)

    return 0


def parse_options():
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-p", "--pass", dest="passwd", help="Как получить пароль для архива. Можно: pass:<пароль>, env:<переменная окружения с паролем>, file:<файл с паролем> (как в openssl). Если ничего не указать -- попросит ввести.", type=str)
    parser.add_argument("--yav-oauth-file", dest="oauth_file", help="файл с oauth токеном для секретницы", type=str)
    parser.add_argument("-h", "--help", dest="help", help="Справка", action="store_true")
    opts, extra = parser.parse_known_args()

    if opts.help:
        print description
        print parser.format_help()
        print "commands:"
        for cmd in sorted(cmds.keys()):
            print "%s:\n  %s" % (cmd, cmds[cmd]['desc'])
        exit(0)

    if opts.oauth_file and not os.path.exists(os.path.expanduser(opts.oauth_file)):
        print u"Файл '%s' не существует" % opts.oauth_file
        sys.exit(1)

    if len(extra) <= 0:
        die("expecting action")

    opts.cmd = extra.pop(0)
    
    if not opts.cmd in cmds:
        die("unknown action '%s'" % opts.cmd)

    opts.extra = extra

    return opts


def get_token(oauth_file=None):
    if os.environ.get("yav_OAUTH", ""):
        return os.environ["yav_OAUTH"]
    elif oauth_file:
        with open(os.path.expanduser(oauth_file), "r") as f:
            return f.read().strip()
    elif os.path.exists(os.path.expanduser("~/.yav-oauth")):
        with open(os.path.expanduser("~/.yav-oauth"), "r") as f:
            return f.read().strip()
    else:
        print u"Не удается найти OAuth токен для секретницы!\nПолучить его можно здесь: https://oauth.yandex-team.ru/authorize?response_type=token&client_id=ce68fbebc76c4ffda974049083729982\nПоложить можно в переменную окружения yav_OAUTH, либо в файл ~/.yav-oauth, либо в любой другой файл и передавать через параметр --oauth-file"
        sys.exit(1)


def run():
    global token

    init_cmds()
    opts = parse_options()
    token = get_token(opts.oauth_file)
    
    do_one_cmd(opts)
    exit(0)


if __name__ == '__main__':
    run();
