Skip to content

Commit

Permalink
Fix/template access (#53)
Browse files Browse the repository at this point in the history
* Fix template visibility

* Remove unused function

* Manage missing visibility in metadata

* Implement new metadata structure and validation
  • Loading branch information
maricaantonacci authored Nov 2, 2023
1 parent 739f889 commit f0cfc52
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 56 deletions.
7 changes: 4 additions & 3 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from flask_migrate import Migrate, upgrade
from flask_caching import Cache
from flask_redis import FlaskRedis
from app.lib.ToscaInfo import ToscaInfo
from app.lib.tosca_info import ToscaInfo
from app.lib.Vault import Vault

import logging
Expand All @@ -48,6 +48,7 @@
app.secret_key = "30bb7cf2-1fef-4d26-83f0-8096b6dcc7a3"
app.config.from_object('config.default')
app.config.from_file('config.json', json.load)
app.config.from_file('../config/schemas/metadata_schema.json', json.load)

if app.config.get("FEATURE_VAULT_INTEGRATION") == "yes":
app.config.from_file('vault-config.json', json.load)
Expand Down Expand Up @@ -87,7 +88,7 @@ def inject_settings():
s3_allowed_groups=app.config.get("S3_IAM_GROUPS") if app.config.get("S3_IAM_GROUPS") else [],
enable_access_request=app.config.get("FEATURE_ACCESS_REQUEST") if app.config.get(
'FEATURE_ACCESS_REQUEST') else "no",
access_request_tag=app.config.get("ACCESS_REQUEST_TAG")
not_granted_access_tag=app.config.get("NOT_GRANTED_ACCESS_TAG")
)


Expand All @@ -107,7 +108,7 @@ def inject_settings():

# initialize ToscaInfo
tosca: ToscaInfo = ToscaInfo(redis_client, app.config.get("TOSCA_TEMPLATES_DIR"),
app.config.get("SETTINGS_DIR"))
app.config.get("SETTINGS_DIR"), app.config.get("METADATA_SCHEMA"))

from app.errors.routes import errors_bp
app.register_blueprint(errors_bp)
Expand Down
21 changes: 10 additions & 11 deletions app/deployments/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,26 @@
# limitations under the License.

import copy

import uuid as uuid_generator
import io
import os
import re
from urllib.parse import urlparse
import yaml
from flask import Blueprint, session, render_template, flash, redirect, url_for, json, request
from packaging import version
from werkzeug.exceptions import Forbidden
from werkzeug.utils import secure_filename
from app import app, iam_blueprint, tosca, vaultservice
from app.lib import auth, utils, settings, dbhelpers, yourls
from app.lib.ldap_user import LdapUserManager
from app.models.Deployment import Deployment
from app.providers import sla
from app.lib import ToscaInfo as tosca_helpers
from app.lib import tosca_info as tosca_helpers
from app.lib import openstack as keystone
from app.lib.orchestrator import Orchestrator
from app.lib import s3 as s3
from werkzeug.exceptions import Forbidden
from werkzeug.utils import secure_filename
from app.swift.swift import Swift
from packaging import version
from urllib.parse import urlparse
import uuid as uuid_generator
import yaml
import io
import os
import re

deployments_bp = Blueprint('deployments_bp', __name__,
template_folder='templates',
Expand Down
2 changes: 1 addition & 1 deletion app/deployments/templates/deployments.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ <h4 class="font-weight-bold text-primary">My deployments</h4>
<span class="fas fa-edit mr-2 grey-text"></span>Edit</a>
<a class="dropdown-item" href="{{ url_for('deployments_bp.deptemplate', depid=deployment.uuid) }}"><span class="fas fa-search mr-2 grey-text"></span>Show template</a>
{% if enable_update_deployment == "yes" and deployment.updatable == 1 %}
<a class="dropdown-item" href="{{ url_for('deployments_bp.depupdate', depid=deployment.uuid) }}"><span class="fas fa-plus mr-2 grey-text"></span>Update</a>
<a class="dropdown-item" href="{{ url_for('deployments_bp.depupdate', depid=deployment.uuid) }}"><span class="fas fa-plus-minus mr-2 grey-text"></span>Add/Remove nodes</a>
{% endif %}
{% if deployment.deployment_type == "CLOUD" %}
<a class="dropdown-item {% if deployment.physicalId is not defined %}disabled{% endif %}" href="{{ url_for('deployments_bp.deplog', depid=deployment.uuid) }}">
Expand Down
43 changes: 25 additions & 18 deletions app/home/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from datetime import datetime
from markupsafe import Markup
from flask import Blueprint, json, render_template, request, redirect, url_for, session, make_response, flash
import json
import re, json

app.jinja_env.filters['tojson_pretty'] = utils.to_pretty_json
app.jinja_env.filters['extract_netinterface_ips'] = utils.extract_netinterface_ips
Expand Down Expand Up @@ -85,7 +85,12 @@ def submit_settings():
if repo_url: dashboard_configuration_info['dashboard_configuration_url'] = repo_url
if tag_or_branch: dashboard_configuration_info['dashboard_configuration_tag_or_branch'] = tag_or_branch

tosca.reload()
try:
tosca.reload()
except Exception as error:
app.logger.error(f"Error reloading configuration: {error}")
flash(f"Error reloading configuration: { type(error).__name__ }. Please check the logs.", "danger")

app.logger.debug("Configuration reloaded")

now = datetime.now()
Expand Down Expand Up @@ -118,25 +123,27 @@ def login():
return render_template(app.config.get('HOME_TEMPLATE'))


def is_template_locked(allowed_groups, user_groups):
# check intersection of user groups with user membership
if (allowed_groups is None or set(allowed_groups.split(',')) & set(user_groups)) != set() or allowed_groups == '*':
return False
else:
return True


def set_template_access(tosca, user_groups, active_group):
info = {}
for k, v in tosca.items():
allowed_groups = v.get("metadata").get("allowed_groups")
if not allowed_groups:
app.logger.error("Null - {}".format(k))
access_locked = is_template_locked(allowed_groups, user_groups)
if (access_locked and ("visibility" not in v.get("metadata") or v["metadata"]["visibility"] == "public")) or (
not access_locked and (active_group in allowed_groups.split(',') or allowed_groups == "*")):
v["metadata"]["access_locked"] = access_locked
info[k] = v
visibility = v.get("metadata").get("visibility") if "visibility" in v.get("metadata") else {"type": "public"}

if visibility.get("type") != "public":

regex = False if "groups_regex" not in visibility else True

if regex:
access_locked = not re.match(visibility.get('groups_regex'), active_group)
else:
allowed_groups = visibility.get("groups")
access_locked = True if active_group not in allowed_groups else False

if (visibility.get("type") == "private" and not access_locked) or visibility.get("type") == "protected":
v["metadata"]["access_locked"] = access_locked
info[k] = v
else:
info[k] = v

return info


Expand Down
12 changes: 11 additions & 1 deletion app/home/templates/portfolio.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@
<div class="row">
<div class="col-md-12">
<p class="font-weight-bold" style="font-size:20px;font-variant:small-caps">On-demand Services: </p>
{% if templates_info.items()|length == 0 %}
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h2 class="display-4">No Available Services for Deployment</h2>
<p class="lead">We're sorry, but no services are currently available for deployment.</p>
<p class="lead">This could be due to specific requirements or settings.</p>
<p class="lead">Please contact our support team for assistance."</p>
</div>
</div>
{% endif %}
{% for tosca_name, tosca in templates_info.items() %}
{% if loop.index % 3 == 1 %}
<!-- open card deck -->
Expand All @@ -39,7 +49,7 @@ <h5 class="card-title text-center">
<img class="card-img-bottom img-fluid" src="{{tosca['metadata']['icon']}}" alt="Card image cap">
{% if tosca['metadata']['access_locked'] %}
<div class="ribbon red">
<span>{{ access_request_tag }}</span>
<span>{{ not_granted_access_tag }}</span>
</div>
{% else %}
{% if tosca['metadata']['tag'] is defined %}
Expand Down
46 changes: 27 additions & 19 deletions app/lib/ToscaInfo.py → app/lib/tosca_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Class to load tosca templates at application start
"""

import json
import os
import io
from fnmatch import fnmatch
import uuid
import yaml
import jsonschema


class ToscaInfo(object):
class ToscaInfo:
"""Class to load tosca templates and metadata at application start"""

def __init__(self, redis_client, tosca_dir=None, settings_dir=None):
def __init__(self, redis_client, tosca_dir, settings_dir, metadata_schema):
"""
Initialize the flask extension
:param tosca_dir: the dir of the tosca templates
Expand All @@ -34,17 +35,15 @@ def __init__(self, redis_client, tosca_dir=None, settings_dir=None):
self.tosca_dir = tosca_dir + '/'
self.tosca_params_dir = settings_dir + '/tosca-parameters'
self.tosca_metadata_dir = settings_dir + '/tosca-metadata'
self.metadata_schema = metadata_schema

tosca_info = {}
tosca_gmetadata = {}
tosca_templates = []

tosca_templates = self._loadtoscatemplates()
tosca_info = self._extractalltoscainfo(tosca_templates)

if os.path.isfile(self.tosca_metadata_dir + "/metadata.yml"):
with io.open(self.tosca_metadata_dir + "/metadata.yml") as stream:
tosca_gmetadata = yaml.full_load(stream)
tosca_gmetadata = self._loadmetadata()

redis_client.set("tosca_templates", json.dumps(tosca_templates))
redis_client.set("tosca_gmetadata", json.dumps(tosca_gmetadata))
Expand All @@ -54,16 +53,24 @@ def __init__(self, redis_client, tosca_dir=None, settings_dir=None):
def reload(self):
tosca_templates = self._loadtoscatemplates()
tosca_info = self._extractalltoscainfo(tosca_templates)
tosca_gmetadata = {}

if os.path.isfile(self.tosca_metadata_dir + "/metadata.yml"):
with io.open(self.tosca_metadata_dir + "/metadata.yml") as stream:
tosca_gmetadata = yaml.full_load(stream)
tosca_gmetadata = self._loadmetadata()

self.redis_client.set("tosca_templates", json.dumps(tosca_templates))
self.redis_client.set("tosca_gmetadata", json.dumps(tosca_gmetadata))
self.redis_client.set("tosca_info", json.dumps(tosca_info))

def _loadmetadata(self):
if os.path.isfile(self.tosca_metadata_dir + "/metadata.yml"):
with io.open(self.tosca_metadata_dir + "/metadata.yml") as stream:
metadata = yaml.full_load(stream)

# validate against schema
jsonschema.validate(metadata, self.metadata_schema, format_checker=jsonschema.Draft202012Validator.FORMAT_CHECKER)
#tosca_gmetadata = {service["id"]: {k: v for k, v in service.items() if k != "id"} for service in metadata['services']}
tosca_gmetadata = {str(uuid.uuid4()): service for service in metadata['services']}
return tosca_gmetadata


def _loadtoscatemplates(self):
toscatemplates = []
for path, subdirs, files in os.walk(self.tosca_dir):
Expand All @@ -81,19 +88,20 @@ def _extractalltoscainfo(self, tosca_templates):
with io.open(self.tosca_dir + tosca) as stream:
template = yaml.full_load(stream)
tosca_info[tosca] = self.extracttoscainfo(template, tosca)
#info = self.extracttoscainfo(template, tosca)
#tosca_info[info.get('id')] = info
return tosca_info

def extracttoscainfo(self, template, tosca):

tosca_info = {
"valid": True,
"description": "TOSCA Template",
"metadata": {
"icon": "https://cdn4.iconfinder.com/data/icons/mosaicon-04/512/websettings-512.png",
"visibility": "public",
"allowed_groups": '*',
"require_ssh_key": True,
"template_type": ""
"visibility": { "type": "public" },
"require_ssh_key": True,
"template_type": ""
},
"enable_config_form": False,
"inputs": {},
Expand Down
2 changes: 1 addition & 1 deletion config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
FEATURE_S3CREDS_MENU = "no"
FEATURE_ACCESS_REQUEST = "yes"

ACCESS_REQUEST_TAG = "LOCKED"
NOT_GRANTED_ACCESS_TAG = "LOCKED"

### VAULT INTEGRATION SETTINGS
VAULT_ROLE = "orchestrator"
Expand Down
2 changes: 0 additions & 2 deletions config/infn-cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
FEATURE_PORTS_REQUEST = "yes"
FEATURE_ACCESS_REQUEST = "no"

ACCESS_REQUEST_TAG = "SYS-ADMIN ONLY"

### Template Paths
HOME_TEMPLATE = 'infn-cloud/home.html'
FOOTER_TEMPLATE = 'infn-cloud/footer.html'
Expand Down
Loading

0 comments on commit f0cfc52

Please sign in to comment.