示例#1
0
def register_services(
        where,  # type: Optional[Str]
        services_dict,  # type: Dict[Str, Dict[Str, Str]]
        cookies,  # type: Str
        message="Register response",  # type: Optional[Str]
):  # type: (...) -> Tuple[bool, Dict[Str, int]]
    """
    Registers services on desired location using provided configurations and access cookies.

    :returns: tuple of overall success and individual http response of each service registration.
    """
    success = True
    svc_url = None
    statuses = dict()
    register_service_url = None
    if where == SERVICES_MAGPIE:
        svc_url_tag = "service_url"
        get_magpie_url() + ServicesAPI.path
    elif where == SERVICES_PHOENIX:
        svc_url_tag = "url"
        register_service_url = get_phoenix_url() + "/services/register"
    else:
        raise ValueError("Unknown location for service registration", where)
    for service_name in services_dict:
        cfg = services_dict[service_name]
        cfg["public"] = bool2str(cfg.get("public"))
        cfg["c4i"] = bool2str(cfg.get("c4i"))
        cfg["url"] = os.path.expandvars(cfg.get("url"))
        if where == SERVICES_MAGPIE:
            svc_url = cfg["url"]
        elif where == SERVICES_PHOENIX:
            svc_url = get_twitcher_protected_service_url(service_name)
        params = "service_name={name}&"         \
                 "{svc_url_tag}={svc_url}&"     \
                 "service_title={cfg[title]}&"  \
                 "public={cfg[public]}&"        \
                 "c4i={cfg[c4i]}&"              \
                 "service_type={cfg[type]}&"    \
                 "register=register"            \
                 .format(name=service_name, cfg=cfg, svc_url_tag=svc_url_tag, svc_url=svc_url)
        service_msg = "{msg} ({svc}) [{url}]".format(msg=message,
                                                     svc=service_name,
                                                     url=svc_url)
        error, http_code = request_curl(register_service_url,
                                        cookies=cookies,
                                        form_params=params,
                                        msg=service_msg)
        statuses[service_name] = http_code
        success = success and not error and (
            (where == SERVICES_PHOENIX and http_code == 200) or
            (where == SERVICES_MAGPIE and http_code == 201))
        time.sleep(CREATE_SERVICE_INTERVAL)
    return success, statuses
示例#2
0
def login_success_external(request, external_user_name, external_id, email, provider_name):
    # type: (Request, Str, Str, Str, Str) -> HTTPException
    """
    Generates the login response in case of successful external provider identification.
    """
    # find possibly already registered user by external_id/provider
    user = ExternalIdentityService.user_by_external_id_and_provider(external_id, provider_name, request.db)
    if user is None:
        # create new user with an External Identity
        user = new_user_external(external_user_name=external_user_name, external_id=external_id,
                                 email=email, provider_name=provider_name, db_session=request.db)
    # set a header to remember user (set-cookie)
    headers = remember(request, user.id)

    # redirect to 'Homepage-Route' header only if corresponding to Magpie host
    if "homepage_route" in request.cookies:
        homepage_route = str(request.cookies["homepage_route"])
    elif "Homepage-Route" in request.headers:
        homepage_route = str(request.headers["Homepage-Route"])
    else:
        homepage_route = "/"
    header_host = urlparse(homepage_route).hostname
    magpie_host = get_magpie_url(request)
    if header_host and header_host != magpie_host:
        ax.raise_http(http_error=HTTPForbidden, detail=s.ProviderSignin_GET_ForbiddenResponseSchema.description)
    if not header_host:
        homepage_route = magpie_host + ("/" if not homepage_route.startswith("/") else "") + homepage_route
    return ax.valid_http(http_success=HTTPFound, detail=s.ProviderSignin_GET_FoundResponseSchema.description,
                         content={"homepage_route": homepage_route},
                         http_kwargs={"location": homepage_route, "headers": headers})
示例#3
0
def verify_user(request):
    # type: (Request) -> HTTPException
    """
    Verifies that a valid user authentication on the pointed ``Magpie`` instance (via configuration) also results into a
    valid user authentication with the current ``Twitcher`` instance to ensure settings match between them.

    :param request: an HTTP request with valid authentication token/cookie credentials.
    :return: appropriate HTTP success or error response with details about the result.
    """
    magpie_url = get_magpie_url(request)
    resp = requests.post(magpie_url + SigninAPI.path,
                         json=request.json,
                         headers={
                             "Content-Type": CONTENT_TYPE_JSON,
                             "Accept": CONTENT_TYPE_JSON
                         })
    if resp.status_code != HTTPOk.code:
        content = {"response": resp.json()}
        return raise_http(HTTPForbidden,
                          detail="Failed Magpie login.",
                          content=content,
                          nothrow=True)  # noqa
    authn_policy = request.registry.queryUtility(IAuthenticationPolicy)  # noqa
    result = authn_policy.cookie.identify(request)
    if result is None:
        return raise_http(
            HTTPForbidden,
            detail="Twitcher login incompatible with Magpie login.",
            nothrow=True)  # noqa
    return valid_http(
        HTTPOk,
        detail="Twitcher login verified successfully with Magpie login.")
示例#4
0
def magpie_update_services_conflict(conflict_services, services_dict,
                                    request_cookies):
    # type: (List[Str], ConfigDict, Dict[Str, Str]) -> Dict[Str, int]
    """
    Resolve conflicting services by name during registration by updating them only if pointing to different URL.
    """
    magpie_url = get_magpie_url()
    statuses = dict()
    for svc_name in conflict_services:
        statuses[svc_name] = 409
        svc_url_new = services_dict[svc_name]["url"]
        svc_url_db = "{magpie}/services/{svc}".format(magpie=magpie_url,
                                                      svc=svc_name)
        svc_resp = requests.get(svc_url_db, cookies=request_cookies)
        svc_info = get_json(svc_resp).get(svc_name)
        svc_url_old = svc_info["service_url"]
        if svc_url_old != svc_url_new:
            svc_info["service_url"] = svc_url_new
            res_svc_put = requests.put(svc_url_db,
                                       data=svc_info,
                                       cookies=request_cookies)
            statuses[svc_name] = res_svc_put.status_code
            print_log(
                "[{url_old}] => [{url_new}] Service URL update ({svc}): {resp}"
                .format(svc=svc_name,
                        url_old=svc_url_old,
                        url_new=svc_url_new,
                        resp=res_svc_put.status_code),
                logger=LOGGER)
    return statuses
示例#5
0
 def __init__(self, request):
     super(MagpieOWSSecurity, self).__init__()
     self.magpie_url = get_magpie_url(request)
     self.settings = get_settings(request)
     self.twitcher_ssl_verify = asbool(
         self.settings.get("twitcher.ows_proxy_ssl_verify", True))
     self.twitcher_protected_path = self.settings.get(
         "twitcher.ows_proxy_protected_path", "/ows")
示例#6
0
def api_schema(request):
    # type: (Request) -> JSON
    """
    Return JSON Swagger specifications of Magpie REST API.
    """
    swagger_base_spec = {
        "host": get_magpie_url(request.registry),
        "schemes": [request.scheme]
    }
    return s.generate_api_schema(swagger_base_spec)
示例#7
0
 def __init__(self, request):
     # type: (Request) -> None
     super(MagpieServiceStore, self).__init__(request)
     self.settings = get_settings(request)
     self.session_factory = request.registry["dbsession_factory"]
     self.magpie_url = get_magpie_url(request)
     self.twitcher_ssl_verify = asbool(
         self.settings.get("twitcher.ows_proxy_ssl_verify", True))
     self.magpie_admin_token = get_admin_cookies(self.settings,
                                                 self.twitcher_ssl_verify)
示例#8
0
def get_homepage(request):
    """
    Magpie API homepage (only if Magpie UI is not enabled).
    """
    body = deepcopy(s.InfoAPI)
    body.update({
        u"title": s.TitleAPI,
        u"name": __meta__.__package__,
        u"documentation": get_magpie_url() + s.SwaggerAPI.path
    })
    return ax.valid_http(httpSuccess=HTTPOk,
                         content=body,
                         contentType=CONTENT_TYPE_JSON,
                         detail=s.Version_GET_OkResponseSchema.description)
示例#9
0
    def __init__(self, request):
        self.request = request
        self.magpie_url = get_magpie_url(self.request)
        self.ui_theme = get_constant("MAGPIE_UI_THEME", self.request)
        self.logged_user = get_logged_user(self.request)

        anonymous = get_constant("MAGPIE_ANONYMOUS_GROUP",
                                 settings_container=self.request)
        admin = get_constant("MAGPIE_ADMIN_GROUP",
                             settings_container=self.request)
        self.__class__.MAGPIE_FIXED_GROUP_MEMBERSHIPS = [
            anonymous
        ]  # special groups membership that cannot be edited
        self.__class__.MAGPIE_FIXED_GROUP_EDITS = [
            anonymous, admin
        ]  # special groups that cannot be edited
        # special users that cannot be deleted
        self.__class__.MAGPIE_FIXED_USERS = [
            get_constant("MAGPIE_ADMIN_USER"),
            get_constant("MAGPIE_ANONYMOUS_USER")
        ]
示例#10
0
def verify_user(request):
    magpie_url = get_magpie_url(request)
    resp = requests.post(magpie_url + SigninAPI.path,
                         json=request.json,
                         headers={
                             "Content-Type": CONTENT_TYPE_JSON,
                             "Accept": CONTENT_TYPE_JSON
                         })
    if resp.status_code != HTTPOk.code:
        content = {"response": resp.json()}
        return raise_http(HTTPForbidden,
                          detail="Failed Magpie login.",
                          content=content,
                          nothrow=True)
    authn_policy = request.registry.queryUtility(IAuthenticationPolicy)
    result = authn_policy.cookie.identify(request)
    if result is None:
        return raise_http(
            HTTPForbidden,
            detail="Twitcher login incompatible with Magpie login.",
            nothrow=True)
    return valid_http(
        HTTPOk,
        detail="Twitcher login verified successfully with Magpie login.")
示例#11
0
def get_hostname(test_item):
    # type: (AnyMagpieTestType) -> Str
    app_or_url = get_app_or_url(test_item)
    if isinstance(app_or_url, TestApp):
        app_or_url = get_magpie_url(app_or_url.app.registry)
    return urlparse(app_or_url).hostname
示例#12
0
def magpie_register_permissions_from_config(permissions_config,
                                            magpie_url=None,
                                            db_session=None):
    # type: (Union[Str, ConfigDict], Optional[Str], Optional[Session]) -> None
    """
    Applies permissions specified in configuration.

    :param permissions_config: file path to 'permissions' config or JSON/YAML equivalent pre-loaded.
    :param magpie_url: URL to magpie instance (when using requests; default: `magpie.url` from this app's config).
    :param db_session: db session to use instead of requests to directly create/remove permissions with config.

    .. seealso::
        `magpie/permissions.cfg` for specific parameters and operational details.
    """
    permissions = _load_config(permissions_config, "permissions")
    if not permissions:
        LOGGER.warning("Permissions configuration are empty.")
        return

    if use_request(db_session):
        magpie_url = magpie_url or get_magpie_url()
        settings = {'magpie.url': magpie_url}
        logging.debug(
            "Editing permissions using requests to [{}]...".format(magpie_url))
        err_msg = "Invalid credentials to register Magpie permissions."
        cookies_or_session = get_admin_cookies(settings, raise_message=err_msg)
    else:
        logging.debug("Editing permissions using db session...")
        cookies_or_session = db_session

    logging.info("Found {} permissions to update.".format(len(permissions)))
    for i, perm_cfg in enumerate(permissions):
        # parameter validation
        if not isinstance(perm_cfg, dict) or not all(
                f in perm_cfg for f in ["permission", "service"]):
            warn_permission(
                "Invalid permission format for [{!s}]".format(perm_cfg), i)
            continue
        if perm_cfg["permission"] not in Permission.values():
            warn_permission(
                "Unknown permission [{!s}]".format(perm_cfg['permission']), i)
            continue
        usr_name = perm_cfg.get("user")
        grp_name = perm_cfg.get("group")
        if not any([usr_name, grp_name]):
            warn_permission("Missing required user and/or group field.", i)
            continue
        if "action" not in perm_cfg:
            warn_permission("Unspecified action",
                            i,
                            trail="using default (create)...")
            perm_cfg["action"] = "create"
        if perm_cfg["action"] not in ["create", "remove"]:
            warn_permission("Unknown action [{!s}]".format(perm_cfg["action"]),
                            i)
            continue

        # retrieve service for permissions validation
        svc_name = perm_cfg["service"]
        if use_request(cookies_or_session):
            svc_path = magpie_url + ServiceAPI.path.format(
                service_name=svc_name)
            svc_resp = requests.get(svc_path, cookies=cookies_or_session)
            if svc_resp.status_code != 200:
                warn_permission("Unknown service [{!s}]".format(svc_name), i)
                continue
            service_info = get_json(svc_resp)[svc_name]
        else:
            transaction.commit(
            )  # force any pending transaction to be applied to find possible dependencies
            svc = models.Service.by_service_name(svc_name,
                                                 db_session=cookies_or_session)
            if not svc:
                warn_permission(
                    "Unknown service [{!s}]. Can't edit permissions without service."
                    .format(svc_name), i)
                continue
            from magpie.api.management.service.service_formats import format_service
            service_info = format_service(svc)

        # apply permission config
        resource_id, found = parse_resource_path(perm_cfg, i, service_info,
                                                 cookies_or_session,
                                                 magpie_url)
        if found:
            if not resource_id:
                resource_id = service_info["resource_id"]
            apply_permission_entry(perm_cfg, i, resource_id,
                                   cookies_or_session, magpie_url)

    if not use_request(cookies_or_session):
        transaction.commit()
    logging.info("Done processing permissions.")
示例#13
0
def parse_resource_path(
        permission_config_entry,  # type: ConfigItem
        entry_index,  # type: int
        service_info,  # type: ConfigItem
        cookies_or_session=None,  # type: CookiesOrSessionType
        magpie_url=None,  # type: Optional[Str]
):  # type: (...) -> Tuple[Optional[int], bool]
    """
    Parses the `resource` field of a permission config entry and retrieves the final resource id. Creates missing
    resources as necessary if they can be automatically resolved.

    If `cookies` are provided, uses requests to a running `Magpie` instance (with `magpie_url`) to apply permission.
    If `session` to db is provided, uses direct db connection instead to apply permission.

    :returns: tuple of found id (if any, `None` otherwise), and success status of the parsing operation (error)
    """
    if not magpie_url and use_request(cookies_or_session):
        raise ValueError(
            "cannot use cookies without corresponding request URL")

    resource = None
    resource_path = permission_config_entry.get("resource", "")
    if resource_path.startswith("/"):
        resource_path = resource_path[1:]
    if resource_path.endswith("/"):
        resource_path = resource_path[:-1]
    if resource_path:
        try:
            svc_name = service_info["service_name"]
            svc_type = service_info["service_type"]
            if use_request(cookies_or_session):
                res_path = get_magpie_url() + ServiceResourcesAPI.path.format(
                    service_name=svc_name)
                res_resp = requests.get(res_path, cookies=cookies_or_session)
                res_dict = get_json(res_resp)[svc_name]["resources"]
            else:
                from magpie.api.management.service.service_formats import format_service_resources
                svc = models.Service.by_service_name(
                    svc_name, db_session=cookies_or_session)
                res_dict = format_service_resources(
                    svc, show_all_children=True, db_session=cookies_or_session)
            parent = res_dict["resource_id"]
            child_resources = res_dict["resources"]
            for res in resource_path.split("/"):
                # search in existing children resources
                if len(child_resources):
                    # noinspection PyTypeChecker
                    res_id = list(
                        filter(
                            lambda r: res in
                            [r, child_resources[r]["resource_name"]],
                            child_resources))
                    if res_id:
                        res_info = child_resources[
                            res_id[0]]  # type: Dict[Str, JSON]
                        child_resources = res_info[
                            "children"]  # update next sub-resource iteration
                        parent = res_info["resource_id"]
                        continue
                # missing resource, attempt creation
                svc_res_types = SERVICE_TYPE_DICT[svc_type].resource_type_names
                type_count = len(svc_res_types)
                if type_count != 1:
                    warn_permission(
                        "Cannot automatically generate resources",
                        entry_index,
                        detail=
                        "Service [{}] of type [{}] allows {} sub-resource types"
                        .format(svc_name, svc_type, type_count))
                    raise Exception(
                        "Missing resource to apply permission.")  # fail fast
                res_type = svc_res_types[0]
                if use_request(cookies_or_session):
                    body = {
                        "resource_name": res,
                        "resource_type": res_type,
                        "parent_id": parent
                    }
                    # noinspection PyUnboundLocalVariable
                    resp = requests.post(res_path,
                                         json=body,
                                         cookies=cookies_or_session)
                else:
                    from magpie.api.management.resource.resource_utils import create_resource
                    resp = create_resource(res,
                                           res,
                                           res_type,
                                           parent,
                                           db_session=cookies_or_session)
                if resp.status_code != 201:
                    resp.raise_for_status()
                child_resources = {}
                parent = get_json(resp)["resource"]["resource_id"]
            resource = parent
            if not resource:
                raise Exception(
                    "Could not extract child resource from resource path.")
        except Exception as ex:
            if isinstance(ex, HTTPException):
                detail = "{} ({}), {}".format(
                    type(ex).__name__, ex.status_code, str(ex))
            else:
                detail = repr(ex)
            warn_permission("Failed resources parsing.",
                            entry_index,
                            detail=detail)
            return None, False
    return resource, True
示例#14
0
def magpie_register_services_with_requests(services_dict,
                                           push_to_phoenix,
                                           username,
                                           password,
                                           provider,
                                           force_update=False,
                                           disable_getcapabilities=False):
    # type: (ConfigDict, bool, Str, Str, Str, bool, bool) -> bool
    """
    Registers magpie services using the provided services configuration.

    :param services_dict: services configuration definition.
    :param push_to_phoenix: push registered Magpie services to Phoenix for synced configurations.
    :param username: login username to use to obtain permissions for services registration.
    :param password: login password to use to obtain permissions for services registration.
    :param provider: login provider to use to obtain permissions for services registration.
    :param force_update: override existing services matched by name
    :param disable_getcapabilities: do not execute 'GetCapabilities' validation for applicable services.
    :return: successful operation status
    """
    magpie_url = get_magpie_url()
    curl_cookies = os.path.join(LOGIN_TMP_DIR, "login_cookie_magpie")
    session = requests.Session()
    success = False
    try:
        # Need to login first as admin
        login_url = magpie_url + SigninAPI.path
        login_data = {
            "user_name": username,
            "password": password,
            "provider_name": provider
        }
        login_loop(login_url, curl_cookies, login_data,
                   "Magpie login response")
        login_resp = session.post(login_url, data=login_data)
        if login_resp.status_code != 200:
            raise_log("Failed login with specified credentials", logger=LOGGER)
        request_cookies = login_resp.cookies

        # Register services
        # Magpie will not overwrite existing services by default, 409 Conflict instead of 201 Created
        success, statuses_register = register_services(
            SERVICES_MAGPIE, services_dict, curl_cookies,
            "Magpie register service")
        # Service URL update if conflicting and requested
        if force_update and not success:
            conflict_services = [
                svc_name for svc_name, http_code in statuses_register.items()
                if http_code == 409
            ]
            statuses_update = magpie_update_services_conflict(
                conflict_services, services_dict, request_cookies)
            statuses_register.update(
                statuses_update)  # update previous statuses with new ones

        # Add 'GetCapabilities' permissions on newly created services to allow 'ping' from Phoenix
        # Phoenix doesn't register the service if it cannot be checked with this request
        magpie_add_register_services_perms(services_dict, statuses_register,
                                           curl_cookies, request_cookies,
                                           disable_getcapabilities)
        session.get(magpie_url + SignoutAPI.path)

        # Push updated services to Phoenix
        if push_to_phoenix:
            success = phoenix_update_services(services_dict)

    except Exception as e:
        print_log(
            "Exception during magpie register services: [{!r}]".format(e),
            logger=LOGGER,
            level=logging.ERROR)
    finally:
        session.cookies.clear()
        if os.path.isfile(curl_cookies):
            os.remove(curl_cookies)
    return success
示例#15
0
def magpie_add_register_services_perms(services, statuses, curl_cookies,
                                       request_cookies,
                                       disable_getcapabilities):
    magpie_url = get_magpie_url()
    login_usr = get_constant("MAGPIE_ANONYMOUS_USER")

    for service_name in services:
        svc_available_perms_url = "{magpie}/services/{svc}/permissions" \
                                  .format(magpie=magpie_url, svc=service_name)
        resp_available_perms = requests.get(svc_available_perms_url,
                                            cookies=request_cookies)
        if resp_available_perms.status_code == 401:
            raise_log("Invalid credentials, cannot update service permissions",
                      exception=ValueError,
                      logger=LOGGER)

        available_perms = get_json(resp_available_perms).get(
            "permission_names", [])
        # only applicable to services supporting "GetCapabilities" request
        if resp_available_perms.status_code and Permission.GET_CAPABILITIES.value in available_perms:

            # enforce 'getcapabilities' permission if available for service just updated (200) or created (201)
            # update 'getcapabilities' permission when the service existed and it allowed
            if (not disable_getcapabilities and statuses[service_name] == 409) \
            or statuses[service_name] == 200 or statuses[service_name] == 201:  # noqa
                svc_anonym_add_perms_url = "{magpie}/users/{usr}/services/{svc}/permissions" \
                                           .format(magpie=magpie_url, usr=login_usr, svc=service_name)
                svc_anonym_perm_data = {
                    "permission_name": Permission.GET_CAPABILITIES.value
                }
                requests.post(svc_anonym_add_perms_url,
                              data=svc_anonym_perm_data,
                              cookies=request_cookies)

            # check service response so Phoenix doesn't refuse registration
            # try with both the 'direct' URL and the 'GetCapabilities' URL
            attempt = 0
            service_info_url = "{magpie}/services/{svc}".format(
                magpie=magpie_url, svc=service_name)
            service_info_resp = requests.get(service_info_url,
                                             cookies=request_cookies)
            service_url = get_json(service_info_resp).get(service_name).get(
                "service_url")
            svc_getcap_url = "{svc_url}/wps?service=WPS&version=1.0.0&request=GetCapabilities" \
                             .format(svc_url=service_url)
            while True:
                service_msg_direct = "Service response ({svc})".format(
                    svc=service_name)
                service_msg_getcap = "Service response ({svc}, GetCapabilities)".format(
                    svc=service_name)
                err, http = request_curl(service_url,
                                         cookies=curl_cookies,
                                         msg=service_msg_direct)
                if not err and http == 200:
                    break
                err, http = request_curl(svc_getcap_url,
                                         cookies=curl_cookies,
                                         msg=service_msg_getcap)
                if not err and http == 200:
                    break
                print_log(
                    "[{url}] Bad response from service '{svc}' retrying after {sec}s..."
                    .format(svc=service_name,
                            url=service_url,
                            sec=GETCAPABILITIES_INTERVAL),
                    logger=LOGGER)
                time.sleep(GETCAPABILITIES_INTERVAL)
                attempt += 1
                if attempt >= GETCAPABILITIES_ATTEMPTS:
                    msg = "[{url}] No response from service '{svc}' after {tries} attempts. Skipping..." \
                          .format(svc=service_name, url=service_url, tries=attempt)
                    print_log(msg, logger=LOGGER)
                    break
示例#16
0
 def url(self, settings=None):
     from magpie.api import schemas as s
     return get_magpie_url(settings) + s.TemporaryUrlAPI.path.format(
         token=self.token)