"""
Provide authentication using a REST call

REST auth can be defined like any other eauth module:

.. code-block:: yaml

    external_auth:
      rest:
        ^url: https://url/for/rest/call
        fred:
          - .*
          - '@runner'

If there are entries underneath the ^url entry then they are merged with any responses
from the REST call.  In the above example, assuming the REST call does not return
any additional ACLs, this will authenticate Fred via a REST call and allow him to
run any execution module and all runners.

The REST call should return a JSON array that maps to a regular eauth YAML
structure of a user as above.

"""


import logging

import salt.utils.http

log = logging.getLogger(__name__)

__virtualname__ = "rest"

cached_acl = {}


def __virtual__():
    return __virtualname__


def _rest_auth_setup():

    if "^url" in __opts__["external_auth"]["rest"]:
        return __opts__["external_auth"]["rest"]["^url"]
    else:
        return False


def auth(username, password):
    """
    REST authentication
    """

    url = _rest_auth_setup()

    data = {"username": username, "password": password}

    # Post to the API endpoint. If 200 is returned then the result will be the ACLs
    # for this user
    result = salt.utils.http.query(
        url, method="POST", data=data, status=True, decode=True
    )
    if result["status"] == 200:
        log.debug("eauth REST call returned 200: %s", result)
        if result["dict"] is not None:
            cached_acl[username] = result["dict"]
        return True
    else:
        log.debug("eauth REST call failed: %s", result)
        return False


def acl(username, **kwargs):
    """
    REST authorization
    """
    salt_eauth_acl = __opts__["external_auth"]["rest"].get(username, [])
    log.debug("acl from salt for user %s: %s", username, salt_eauth_acl)

    eauth_rest_acl = cached_acl.get(username, [])
    log.debug("acl from cached rest for user %s: %s", username, eauth_rest_acl)
    # This might be an ACL only call with no auth before, so check the rest api
    # again
    if not eauth_rest_acl:
        # Update cached_acl from REST API
        result = auth(username, kwargs["password"])
        log.debug("acl rest result: %s", result)
        if result:
            eauth_rest_acl = cached_acl.get(username, [])
            log.debug("acl from rest for user %s: %s", username, eauth_rest_acl)

    merged_acl = salt_eauth_acl + eauth_rest_acl

    log.debug("acl from salt and rest merged for user %s: %s", username, merged_acl)
    # We have to make the .get's above return [] since we can't merge a
    # possible list and None. So if merged_acl is falsey we return None so
    # other eauth's can return an acl.
    if not merged_acl:
        return None
    else:
        return merged_acl

@@ -750,25 +750,8 @@
 t%22%0A%0A
-cached_acl = %7B%7D%0A%0A
 %0Adef
@@ -966,19 +966,20 @@
 e%0A%0A%0Adef 
-aut
+fetc
 h(userna
@@ -1001,28 +1001,37 @@
     %22%22%22%0A    
-REST
+Call the rest
  authenticat
@@ -1029,24 +1029,33 @@
 thentication
+ endpoint
 %0A    %22%22%22%0A%0A  
@@ -1051,17 +1051,16 @@
     %22%22%22%0A
-%0A
     url 
@@ -1348,16 +1348,17 @@
 e%0A    )%0A
+%0A
     if r
@@ -1435,32 +1435,81 @@
 0: %25s%22, result)%0A
+        # Call is successful if None no acl data%0A
         if resul
@@ -1547,30 +1547,14 @@
     
-cached_acl%5Busername%5D =
+return
  res
@@ -1565,16 +1565,34 @@
 %22dict%22%5D%0A
+        else:%0A    
         
@@ -1598,20 +1598,18 @@
  return 
-True
+%5B%5D
 %0A    els
@@ -1695,18 +1695,19 @@
 %0A%0A%0Adef a
-cl
+uth
 (usernam
@@ -1713,16 +1713,16 @@
 me, 
-**kwargs
+password
 ):%0A 
@@ -1745,12 +1745,13 @@
 auth
-oriz
+entic
 atio
@@ -1768,182 +1768,383 @@
     
-salt_eauth_acl = __opts__%5B%22external_auth%22%5D%5B%22rest%22%5D.get(username, %5B%5D)%0A    log.debug(%22acl from salt for user %25s: %25s%22, username, salt_eauth_acl)%0A%0A    eauth_rest_acl = cached_acl
+# Check auth on API endpoint%0A    result = fetch(username, password)%0A    if result is False:%0A        log.debug(%22eauth REST call failed: %25s%22, result)%0A        return False%0A    else:%0A        log.debug(%22eauth REST call Ok: %25s%22, result)%0A        return True%0A%0A%0Adef acl(username, **kwargs):%0A    %22%22%22%0A    REST authorization%0A    %22%22%22%0A    salt_eauth_acl = __opts__%5B%22external_auth%22%5D%5B%22rest%22%5D
 .get
@@ -2186,18 +2186,11 @@
 rom 
-cached res
+sal
 t fo
@@ -2219,189 +2219,77 @@
 me, 
+salt_
 eauth_
-rest_
 acl)%0A
+%0A
     # 
-This might be an ACL only call with no auth before, so check the rest api%0A    # again%0A    if not eauth_rest_acl:%0A        # Update cached_acl from REST API%0A    
+Get ACL from REST API%0A    eauth_rest_acl = %5B%5D%0A
     
@@ -2297,19 +2297,20 @@
 esult = 
-aut
+fetc
 h(userna
@@ -2337,61 +2337,8 @@
 %22%5D)%0A
-        log.debug(%22acl rest result: %25s%22, result)%0A    
     
@@ -2356,20 +2356,16 @@
         
-   
- 
 eauth_re
@@ -2377,41 +2377,15 @@
 l = 
-cached_acl.get(username, %5B%5D)%0A    
+result%0A
     

Remove acl cache