diff options
Diffstat (limited to 'src')
35 files changed, 1273 insertions, 197 deletions
diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile deleted file mode 100644 index c47d6229e..000000000 --- a/src/main/docker/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM openjdk:8u191-jdk-alpine3.9
-
-MAINTAINER "The Onap Team"
-LABEL Description="This immage contains alpine, openjdk 11 and clamp"
-
-ARG http_proxy
-ARG https_proxy
-ENV HTTP_PROXY=$http_proxy
-ENV HTTPS_PROXY=$https_proxy
-ENV http_proxy=$HTTP_PROXY
-ENV https_proxy=$HTTPS_PROXY
-
-RUN addgroup onap && adduser -D -G onap clamp
-VOLUME /opt/clamp/config
-RUN mkdir /var/log/onap
-RUN chmod a+rwx /var/log/onap
-
-COPY onap-clamp/clamp.jar /opt/clamp/app.jar
-RUN chmod 700 /opt/clamp/app.jar
-
-RUN chown -R clamp:onap /opt/clamp
-
-RUN apk add fontconfig
-RUN apk add ttf-dejavu
-
-RUN ln -s /usr/lib/libfontconfig.so.1 /usr/lib/libfontconfig.so && \
- ln -s /lib/libuuid.so.1 /usr/lib/libuuid.so.1 && \
- ln -s /lib/libc.musl-x86_64.so.1 /usr/lib/libc.musl-x86_64.so.1
-
-ENV LD_LIBRARY_PATH /usr/lib
-
-USER clamp
-WORKDIR /opt/clamp/
-ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Xms256m", "-Xmx1g", "-jar" ,"./app.jar"]
diff --git a/src/main/docker/backend/Dockerfile b/src/main/docker/backend/Dockerfile new file mode 100644 index 000000000..9e5c8d8b1 --- /dev/null +++ b/src/main/docker/backend/Dockerfile @@ -0,0 +1,57 @@ +###
+# ============LICENSE_START=======================================================
+# ONAP CLAMP
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. 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.
+# ============LICENSE_END============================================
+# ===================================================================
+#
+###
+
+FROM openjdk:8u191-jdk-alpine3.9
+
+MAINTAINER "The Onap Team"
+LABEL Description="This immage contains alpine, openjdk 11 and clamp"
+
+ARG http_proxy
+ARG https_proxy
+ENV HTTP_PROXY=$http_proxy
+ENV HTTPS_PROXY=$https_proxy
+ENV http_proxy=$HTTP_PROXY
+ENV https_proxy=$HTTPS_PROXY
+
+RUN addgroup onap && adduser -D -G onap clamp
+VOLUME /opt/clamp/config
+RUN mkdir /var/log/onap
+RUN chmod a+rwx /var/log/onap
+
+COPY onap-clamp-backend/clamp.jar /opt/clamp/app.jar
+RUN chmod 700 /opt/clamp/app.jar
+
+RUN chown -R clamp:onap /opt/clamp
+
+RUN apk add fontconfig
+RUN apk add ttf-dejavu
+
+RUN ln -s /usr/lib/libfontconfig.so.1 /usr/lib/libfontconfig.so && \
+ ln -s /lib/libuuid.so.1 /usr/lib/libuuid.so.1 && \
+ ln -s /lib/libc.musl-x86_64.so.1 /usr/lib/libc.musl-x86_64.so.1
+
+ENV LD_LIBRARY_PATH /usr/lib
+
+USER clamp
+WORKDIR /opt/clamp/
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Xms256m", "-Xmx1g", "-jar" ,"./app.jar"]
diff --git a/src/main/docker/assembly/clamp-files.xml b/src/main/docker/backend/backend-files.xml index e4e9875c5..7a9c6f530 100644 --- a/src/main/docker/assembly/clamp-files.xml +++ b/src/main/docker/backend/backend-files.xml @@ -1,6 +1,6 @@ <!-- ============LICENSE_START======================================================= - ECOMP MSO + ECOMP CLAMP ================================================================================ Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. ================================================================================ @@ -29,7 +29,6 @@ </formats> <includeBaseDirectory>false</includeBaseDirectory> - <fileSets> <!-- include config files --> <fileSet> @@ -39,20 +38,6 @@ <directory>${project.build.directory}</directory> <outputDirectory>/</outputDirectory> </fileSet> - <fileSet> - <includes> - <include>>etc/config/**</include> - </includes> - <directory>${project.build.directory}</directory> - <outputDirectory>/</outputDirectory> - </fileSet> - <fileSet> - <includes> - <include>etc/keystore/**</include> - </includes> - <directory>${project.build.directory}</directory> - <outputDirectory>/</outputDirectory> - </fileSet> </fileSets> </assembly> diff --git a/src/main/docker/elasticsearch/Dockerfile b/src/main/docker/elasticsearch/Dockerfile new file mode 100644 index 000000000..2c932c8e9 --- /dev/null +++ b/src/main/docker/elasticsearch/Dockerfile @@ -0,0 +1,20 @@ +# https://github.com/elastic/elasticsearch-docker +FROM docker.elastic.co/elasticsearch/elasticsearch:6.6.2 + +COPY config/sg/ config/sg/ +COPY config/ config/ +COPY bin/ bin/ +#RUN chmod +x bin/init_sg.sh +COPY my-entrypoint.sh /usr/local/bin/my-entrypoint.sh +# Search Guard plugin +RUN elasticsearch-plugin install --batch com.floragunn:search-guard-6:6.6.2-25.1 \ + && chmod +x plugins/search-guard-6/tools/*.sh \ + && chown -R elasticsearch config/sg/ \ + && chmod -R go= config/sg/ + +# Add your elasticsearch plugins setup here +# Example: RUN elasticsearch-plugin install analysis-icu + +# This custom entrypoint script is used instead of +# the original's /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["bash","-c","my-entrypoint.sh"]
\ No newline at end of file diff --git a/src/main/docker/elasticsearch/bin/init_sg.sh b/src/main/docker/elasticsearch/bin/init_sg.sh new file mode 100644 index 000000000..1c4e607c6 --- /dev/null +++ b/src/main/docker/elasticsearch/bin/init_sg.sh @@ -0,0 +1,7 @@ +#!/bin/sh +plugins/search-guard-6/tools/sgadmin.sh \ + -cd config/sg/ \ + -ts config/sg/truststore.jks \ + -ks config/sg/kirk-keystore.jks \ + -nhnv \ + -icl
\ No newline at end of file diff --git a/src/main/docker/elasticsearch/config/elasticsearch.yml b/src/main/docker/elasticsearch/config/elasticsearch.yml new file mode 100644 index 000000000..72ce137ab --- /dev/null +++ b/src/main/docker/elasticsearch/config/elasticsearch.yml @@ -0,0 +1,33 @@ +--- +## Default Elasticsearch configuration from elasticsearch-docker. +## from https://github.com/elastic/elasticsearch-docker/blob/master/.tedi/template/elasticsearch.yml +# +cluster.name: "docker-cluster" +network.host: 0.0.0.0 + +# minimum_master_nodes need to be explicitly set when bound on a public IP +# set to 1 to allow single node clusters +# Details: https://github.com/elastic/elasticsearch/pull/17288 +discovery.zen.minimum_master_nodes: 1 + +## Use single node discovery in order to disable production mode and avoid bootstrap checks +## see https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html +# +discovery.type: single-node + +## X-Pack settings +## see https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-xpack.html +# +xpack.license.self_generated.type: basic +xpack.security.enabled: false + +## Search Guard +# +searchguard.enterprise_modules_enabled: false + +searchguard.ssl.transport.keystore_filepath: sg/node-0-keystore.jks +searchguard.ssl.transport.truststore_filepath: sg/truststore.jks +searchguard.ssl.transport.enforce_hostname_verification: false + +searchguard.authcz.admin_dn: + - "CN=kirk,OU=client,O=client,l=tEst,C=De" diff --git a/src/main/docker/elasticsearch/config/sg/kirk-keystore.jks b/src/main/docker/elasticsearch/config/sg/kirk-keystore.jks Binary files differnew file mode 100644 index 000000000..dd7562ef8 --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/kirk-keystore.jks diff --git a/src/main/docker/elasticsearch/config/sg/node-0-keystore.jks b/src/main/docker/elasticsearch/config/sg/node-0-keystore.jks Binary files differnew file mode 100644 index 000000000..5693b7bf8 --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/node-0-keystore.jks diff --git a/src/main/docker/elasticsearch/config/sg/sg_action_groups.yml b/src/main/docker/elasticsearch/config/sg/sg_action_groups.yml new file mode 100644 index 000000000..38ffef024 --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/sg_action_groups.yml @@ -0,0 +1,153 @@ +UNLIMITED: + readonly: true + permissions: + - "*" + +###### INDEX LEVEL ###### + +INDICES_ALL: + readonly: true + permissions: + - "indices:*" + +# for backward compatibility +ALL: + readonly: true + permissions: + - INDICES_ALL + +MANAGE: + readonly: true + permissions: + - "indices:monitor/*" + - "indices:admin/*" + +CREATE_INDEX: + readonly: true + permissions: + - "indices:admin/create" + - "indices:admin/mapping/put" + +MANAGE_ALIASES: + readonly: true + permissions: + - "indices:admin/aliases*" + +INDEX_OWNER: + - CREATE_INDEX + - CRUD + +# for backward compatibility +MONITOR: + readonly: true + permissions: + - INDICES_MONITOR + +INDICES_MONITOR: + readonly: true + permissions: + - "indices:monitor/*" + +DATA_ACCESS: + readonly: true + permissions: + - "indices:data/*" + - CRUD + +WRITE: + readonly: true + permissions: + - "indices:data/write*" + - "indices:admin/mapping/put" + +READ: + readonly: true + permissions: + - "indices:data/read*" + - "indices:admin/mappings/fields/get*" + +DELETE: + readonly: true + permissions: + - "indices:data/write/delete*" + +CRUD: + readonly: true + permissions: + - READ + - WRITE + +SEARCH: + readonly: true + permissions: + - "indices:data/read/search*" + - "indices:data/read/msearch*" + - SUGGEST + +SUGGEST: + readonly: true + permissions: + - "indices:data/read/suggest*" + +INDEX: + readonly: true + permissions: + - "indices:data/write/index*" + - "indices:data/write/update*" + - "indices:admin/mapping/put" + - "indices:data/write/bulk*" + +GET: + readonly: true + permissions: + - "indices:data/read/get*" + - "indices:data/read/mget*" + +###### CLUSTER LEVEL ###### + +CLUSTER_ALL: + readonly: true + permissions: + - "cluster:*" + +CLUSTER_MONITOR: + readonly: true + permissions: + - "cluster:monitor/*" + +CLUSTER_COMPOSITE_OPS_RO: + readonly: true + permissions: + - "indices:data/read/mget" + - "indices:data/read/msearch" + - "indices:data/read/mtv" + - "indices:admin/aliases/exists*" + - "indices:admin/aliases/get*" + - "indices:data/read/scroll" + +CLUSTER_COMPOSITE_OPS: + readonly: true + permissions: + - "indices:data/write/bulk" + - "indices:admin/aliases*" + - "indices:data/write/reindex" + - CLUSTER_COMPOSITE_OPS_RO + +MANAGE_SNAPSHOTS: + readonly: true + permissions: + - "cluster:admin/snapshot/*" + - "cluster:admin/repository/*" + +# CLAMPDASHBOARD ELASTICSEARCH ENTRYPOINT +GET_TEMPLATE: + - "indices:admin/template/get" +PUT_TEMPLATE: + - "indices:admin/template/put" +TEMPLATE_OWNER: + - GET_TEMPLATE + - PUT_TEMPLATE +ES_INPUT: + - TEMPLATE_OWNER + - WRITE + - MONITOR diff --git a/src/main/docker/elasticsearch/config/sg/sg_config.yml b/src/main/docker/elasticsearch/config/sg/sg_config.yml new file mode 100644 index 000000000..7d3a933fa --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/sg_config.yml @@ -0,0 +1,238 @@ +# This is the main Search Guard configuration file where authentication +# and authorization is defined. +# +# You need to configure at least one authentication domain in the authc of this file. +# An authentication domain is responsible for extracting the user credentials from +# the request and for validating them against an authentication backend like Active Directory for example. +# +# If more than one authentication domain is configured the first one which succeeds wins. +# If all authentication domains fail then the request is unauthenticated. +# In this case an exception is thrown and/or the HTTP status is set to 401. +# +# After authentication authorization (authz) will be applied. There can be zero or more authorizers which collect +# the roles from a given backend for the authenticated user. +# +# Both, authc and auth can be enabled/disabled separately for REST and TRANSPORT layer. Default is true for both. +# http_enabled: true +# transport_enabled: true +# +# 5.x Migration: "enabled: true/false" will also be respected currently but only to provide backward compatibility. +# +# For HTTP it is possible to allow anonymous authentication. If that is the case then the HTTP authenticators try to +# find user credentials in the HTTP request. If credentials are found then the user gets regularly authenticated. +# If none can be found the user will be authenticated as an "anonymous" user. This user has always the username "sg_anonymous" +# and one role named "sg_anonymous_backendrole". +# If you enable anonymous authentication all HTTP authenticators will not challenge. +# +# +# Note: If you define more than one HTTP authenticators make sure to put non-challenging authenticators like "proxy" or "clientcert" +# first and the challenging one last. +# Because it's not possible to challenge a client with two different authentication methods (for example +# Kerberos and Basic) only one can have the challenge flag set to true. You can cope with this situation +# by using pre-authentication, e.g. sending a HTTP Basic authentication header in the request. +# +# Default value of the challenge flag is true. +# +# +# HTTP +# basic (challenging) +# proxy (not challenging, needs xff) +# kerberos (challenging) NOT FREE FOR COMMERCIAL +# clientcert (not challenging, needs https) +# jwt (not challenging) NOT FREE FOR COMMERCIAL +# host (not challenging) #DEPRECATED, will be removed in a future version. +# host based authentication is configurable in sg_roles_mapping + +# Authc +# internal +# noop +# ldap NOT FREE FOR COMMERCIAL USE + +# Authz +# ldap NOT FREE FOR COMMERCIAL USE +# noop + +searchguard: + dynamic: + # Set filtered_alias_mode to 'disallow' to forbid more than 2 filtered aliases per index + # Set filtered_alias_mode to 'warn' to allow more than 2 filtered aliases per index but warns about it (default) + # Set filtered_alias_mode to 'nowarn' to allow more than 2 filtered aliases per index silently + #filtered_alias_mode: warn + #kibana: + # Kibana multitenancy - NOT FREE FOR COMMERCIAL USE + # see https://github.com/floragunncom/search-guard-docs/blob/master/multitenancy.md + # To make this work you need to install https://github.com/floragunncom/search-guard-module-kibana-multitenancy/wiki + #multitenancy_enabled: true + #server_username: kibanaserver + #index: '.kibana' + #do_not_fail_on_forbidden: false + http: + anonymous_auth_enabled: false + xff: + enabled: false + internalProxies: '192\.168\.0\.10|192\.168\.0\.11' # regex pattern + #internalProxies: '.*' # trust all internal proxies, regex pattern + remoteIpHeader: 'x-forwarded-for' + proxiesHeader: 'x-forwarded-by' + #trustedProxies: '.*' # trust all external proxies, regex pattern + ###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help + ###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For + ###### and here https://tools.ietf.org/html/rfc7239 + ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve + authc: + kerberos_auth_domain: + http_enabled: false + transport_enabled: false + order: 6 + http_authenticator: + type: kerberos # NOT FREE FOR COMMERCIAL USE + challenge: true + config: + # If true a lot of kerberos/security related debugging output will be logged to standard out + krb_debug: false + # If true then the realm will be stripped from the user name + strip_realm_from_principal: true + authentication_backend: + type: noop + basic_internal_auth_domain: + http_enabled: true + transport_enabled: true + order: 4 + http_authenticator: + type: basic + challenge: true + authentication_backend: + type: intern + proxy_auth_domain: + http_enabled: false + transport_enabled: false + order: 3 + http_authenticator: + type: proxy + challenge: false + config: + user_header: "x-proxy-user" + roles_header: "x-proxy-roles" + authentication_backend: + type: noop + jwt_auth_domain: + http_enabled: false + transport_enabled: false + order: 0 + http_authenticator: + type: jwt + challenge: false + config: + signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key" + jwt_header: "Authorization" + jwt_url_parameter: null + roles_key: null + subject_key: null + authentication_backend: + type: noop + clientcert_auth_domain: + http_enabled: false + transport_enabled: false + order: 2 + http_authenticator: + type: clientcert + config: + username_attribute: cn #optional, if omitted DN becomes username + challenge: false + authentication_backend: + type: noop + ldap: + http_enabled: false + transport_enabled: false + order: 5 + http_authenticator: + type: basic + challenge: false + authentication_backend: + # LDAP authentication backend (authenticate users against a LDAP or Active Directory) + type: ldap # NOT FREE FOR COMMERCIAL USE + config: + # enable ldaps + enable_ssl: false + # enable start tls, enable_ssl should be false + enable_start_tls: false + # send client certificate + enable_ssl_client_auth: false + # verify ldap hostname + verify_hostnames: true + hosts: + - localhost:8389 + bind_dn: null + password: null + userbase: 'ou=people,dc=example,dc=com' + # Filter to search for users (currently in the whole subtree beneath userbase) + # {0} is substituted with the username + usersearch: '(sAMAccountName={0})' + # Use this attribute from the user as username (if not set then DN is used) + username_attribute: null + authz: + roles_from_myldap: + http_enabled: false + transport_enabled: false + authorization_backend: + # LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too) + type: ldap # NOT FREE FOR COMMERCIAL USE + config: + # enable ldaps + enable_ssl: false + # enable start tls, enable_ssl should be false + enable_start_tls: false + # send client certificate + enable_ssl_client_auth: false + # verify ldap hostname + verify_hostnames: true + hosts: + - localhost:8389 + bind_dn: null + password: null + rolebase: 'ou=groups,dc=example,dc=com' + # Filter to search for roles (currently in the whole subtree beneath rolebase) + # {0} is substituted with the DN of the user + # {1} is substituted with the username + # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute + rolesearch: '(member={0})' + # Specify the name of the attribute which value should be substituted with {2} above + userroleattribute: null + # Roles as an attribute of the user entry + userrolename: disabled + #userrolename: memberOf + # The attribute in a role entry containing the name of that role, Default is "name". + # Can also be "dn" to use the full DN as rolename. + rolename: cn + # Resolve nested roles transitive (roles which are members of other roles and so on ...) + resolve_nested_roles: true + userbase: 'ou=people,dc=example,dc=com' + # Filter to search for users (currently in the whole subtree beneath userbase) + # {0} is substituted with the username + usersearch: '(uid={0})' + # Skip users matching a user name, a wildcard or a regex pattern + #skip_users: + # - 'cn=Michael Jackson,ou*people,o=TEST' + # - '/\S*/' + roles_from_another_ldap: + enabled: false + authorization_backend: + type: ldap # NOT FREE FOR COMMERCIAL USE + #config goes here ... +# auth_failure_listeners: +# ip_rate_limiting: +# type: ip +# allowed_tries: 10 +# time_window_seconds: 3600 +# block_expiry_seconds: 600 +# max_blocked_clients: 100000 +# max_tracked_clients: 100000 +# internal_authentication_backend_limiting: +# type: username +# authentication_backend: intern +# allowed_tries: 10 +# time_window_seconds: 3600 +# block_expiry_seconds: 600 +# max_blocked_clients: 100000 +# max_tracked_clients: 100000 +
\ No newline at end of file diff --git a/src/main/docker/elasticsearch/config/sg/sg_internal_users.yml b/src/main/docker/elasticsearch/config/sg/sg_internal_users.yml new file mode 100644 index 000000000..1712d3792 --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/sg_internal_users.yml @@ -0,0 +1,45 @@ +# This is the internal user database +# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh + +#password is: admin +admin: + readonly: true + hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG + roles: + - admin + attributes: + #no dots allowed in attribute names + attribute1: value1 + attribute2: value2 + attribute3: value3 + +#password is: logstash +logstash: + hash: $2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2 + roles: + - logstash + +#password is: kibanaserver +kibanaserver: + readonly: true + hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. + +#password is: kibanaro +kibanaro: + hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC + roles: + - kibanauser + - readall + +#password is: readall +readall: + hash: $2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2 + #password is: readall + roles: + - readall + +#password is: snapshotrestore +snapshotrestore: + hash: $2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W + roles: + - snapshotrestore
\ No newline at end of file diff --git a/src/main/docker/elasticsearch/config/sg/sg_roles.yml b/src/main/docker/elasticsearch/config/sg/sg_roles.yml new file mode 100644 index 000000000..6902fba2c --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/sg_roles.yml @@ -0,0 +1,304 @@ +#<sg_role_name>: +# cluster: +# - '<permission>' +# indices: +# '<indexname or alias>': +# '<type>': +# - '<permission>' +# _dls_: '<dls query>' +# _fls_: +# - '<field>' +# - '<field>' + +# When a user make a request to Elasticsearch then the following roles will be evaluated to see if the user has +# permissions for the request. A request is always associated with an action and is executed against and index (or alias) +# and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. +# Every role a user has will be examined if it allows the action against an index (or type). At least one role must match +# for the request to be successful. If no role match then the request will be denied. Currently a match must happen within +# one single role - that means that permissions can not span multiple roles. + +# For <permission>, <indexname or alias> and <type> simple wildcards and regular expressions are possible. +# A asterix (*) will match any character sequence (or an empty sequence) +# A question mark (?) will match any single character (but NOT empty character) +# Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' +# Example: '?kibana' will match '.kibana' but not 'kibana' + +# To use a full blown regex you have to pre- and apend a '/' to use regex instead of simple wildcards +# '/<java regex>/' +# Example: '/\S*/' will match any non whitespace characters + +# Important: +# Index, alias or type names can not contain dots (.) in the <indexname or alias> or <type> expression. +# Reason is that we currently parse the config file into a elasticsearch settings object which cannot cope with dots in keys. +# Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index' +# This limitation will likely removed with Search Guard 6 + +# DLS (Document level security) - NOT FREE FOR COMMERCIAL +# http://docs.search-guard.com/v6/document-level-security + +# FLS (Field level security) - NOT FREE FOR COMMERCIAL +# http://docs.search-guard.com/v6/field-level-security + +# Kibana multitenancy - NOT FREE FOR COMMERCIAL +# http://docs.search-guard.com/v6/kibana-multi-tenancy + +# Allows everything, but no changes to searchguard configuration index +sg_all_access: + readonly: true + cluster: + - UNLIMITED + indices: + '*': + '*': + - UNLIMITED + tenants: + admin_tenant: RW + +# Read all, but no write permissions +sg_readall: + readonly: true + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + '*': + '*': + - READ + +# Read all and monitor, but no write permissions +sg_readall_and_monitor: + cluster: + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS_RO + indices: + '*': + '*': + - READ + +# For users which use kibana, access to indices must be granted separately +sg_kibana_user: + readonly: true + cluster: + - INDICES_MONITOR + - CLUSTER_COMPOSITE_OPS + indices: + '?kibana': + '*': + - MANAGE + - INDEX + - READ + - DELETE + '?kibana-6': + '*': + - MANAGE + - INDEX + - READ + - DELETE + '?kibana_*': + '*': + - MANAGE + - INDEX + - READ + - DELETE + '?tasks': + '*': + - INDICES_ALL + '?management-beats': + '*': + - INDICES_ALL + '*': + '*': + - indices:data/read/field_caps* + - indices:data/read/xpack/rollup* + - indices:admin/mappings/get* + - indices:admin/get + +# For the kibana server +sg_kibana_server: + readonly: true + cluster: + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + - cluster:admin/xpack/monitoring* + - indices:admin/template* + - indices:data/read/scroll* + indices: + '?kibana': + '*': + - INDICES_ALL + '?kibana-6': + '*': + - INDICES_ALL + '?kibana_*': + '*': + - INDICES_ALL + '?reporting*': + '*': + - INDICES_ALL + '?monitoring*': + '*': + - INDICES_ALL + '?tasks': + '*': + - INDICES_ALL + '?management-beats*': + '*': + - INDICES_ALL + '*': + '*': + - "indices:admin/aliases*" + +# For logstash and beats +sg_logstash: + cluster: + - ES_INPUT + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + - indices:admin/template/get + - indices:admin/template/put + indices: + 'logstash-*': + '*': + - INDEX_OWNER + '*beat*': + '*': + - INDEX_OWNER + 'dmaap*': + '*': + - INDEX_OWNER + 'events*': + '*': + - INDEX_OWNER + 'errors*': + '*': + - INDEX_OWNER + +# Allows adding and modifying repositories and creating and restoring snapshots +sg_manage_snapshots: + cluster: + - MANAGE_SNAPSHOTS + indices: + '*': + '*': + - "indices:data/write/index" + - "indices:admin/create" + +# Allows each user to access own named index +sg_own_index: + cluster: + - CLUSTER_COMPOSITE_OPS + indices: + '${user_name}': + '*': + - INDICES_ALL + +### X-Pack COMPATIBILITY +sg_xp_monitoring: + readonly: true + cluster: + - cluster:monitor/xpack/info + - cluster:monitor/main + - cluster:admin/xpack/monitoring/bulk + indices: + '?monitor*': + '*': + - INDICES_ALL + +sg_xp_alerting: + readonly: true + cluster: + - indices:data/read/scroll + - cluster:admin/xpack/watcher* + - cluster:monitor/xpack/watcher* + indices: + '?watches*': + '*': + - INDICES_ALL + '?watcher-history-*': + '*': + - INDICES_ALL + '?triggered_watches': + '*': + - INDICES_ALL + '*': + '*': + - READ + - indices:admin/aliases/get + +sg_xp_machine_learning: + readonly: true + cluster: + - cluster:admin/persistent* + - cluster:internal/xpack/ml* + - indices:data/read/scroll* + - cluster:admin/xpack/ml* + - cluster:monitor/xpack/ml* + indices: + '*': + '*': + - READ + - indices:admin/get* + '?ml-*': + '*': + - "*" + + +### LEGACY ROLES, FOR COMPATIBILITY ONLY +### WILL BE REMOVED IN SG7, DO NOT USE ANYMORE + +sg_readonly_and_monitor: + cluster: + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS_RO + indices: + '*': + '*': + - READ + +# Make xpack monitoring work +sg_monitor: + cluster: + - cluster:admin/xpack/monitoring/* + - cluster:admin/ingest/pipeline/put + - cluster:admin/ingest/pipeline/get + - indices:admin/template/get + - indices:admin/template/put + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + indices: + '?monitor*': + '*': + - INDICES_ALL + '?marvel*': + '*': + - INDICES_ALL + '?kibana*': + '*': + - READ + '*': + '*': + - indices:data/read/field_caps + +# Make xpack alerting work +sg_alerting: + cluster: + - indices:data/read/scroll + - cluster:admin/xpack/watcher/watch/put + - cluster:admin/xpack/watcher* + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + indices: + '?kibana*': + '*': + - READ + '?watches*': + '*': + - INDICES_ALL + '?watcher-history-*': + '*': + - INDICES_ALL + '?triggered_watches': + '*': + - INDICES_ALL + '*': + '*': + - READ diff --git a/src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml b/src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml new file mode 100644 index 000000000..45bb77556 --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/sg_roles_mapping.yml @@ -0,0 +1,34 @@ +# In this file users, backendroles and hosts can be mapped to Search Guard roles. +# Permissions for Search Guard roles are configured in sg_roles.yml + +sg_all_access: + readonly: true + backendroles: + - admin + +sg_logstash: + backendroles: + - logstash + +sg_kibana_server: + readonly: true + users: + - kibanaserver + +sg_kibana_user: + backendroles: + - kibanauser + +sg_readall: + readonly: true + backendroles: + - readall + +sg_manage_snapshots: + readonly: true + backendroles: + - snapshotrestore + +sg_own_index: + users: + - '*'
\ No newline at end of file diff --git a/src/main/docker/elasticsearch/config/sg/truststore.jks b/src/main/docker/elasticsearch/config/sg/truststore.jks Binary files differnew file mode 100644 index 000000000..7a1b59a8d --- /dev/null +++ b/src/main/docker/elasticsearch/config/sg/truststore.jks diff --git a/src/main/docker/elasticsearch/my-entrypoint.sh b/src/main/docker/elasticsearch/my-entrypoint.sh new file mode 100644 index 000000000..9eee64e0c --- /dev/null +++ b/src/main/docker/elasticsearch/my-entrypoint.sh @@ -0,0 +1,8 @@ +source init_sg.sh + +while [ $? -ne 0 ]; do + sleep 10 + source init_sg.sh +done & + +/bin/bash -c "source /usr/local/bin/docker-entrypoint.sh;" diff --git a/src/main/docker/frontend/Dockerfile b/src/main/docker/frontend/Dockerfile new file mode 100644 index 000000000..8c755b039 --- /dev/null +++ b/src/main/docker/frontend/Dockerfile @@ -0,0 +1,52 @@ +###
+# ============LICENSE_START=======================================================
+# ONAP CLAMP
+# ================================================================================
+# Copyright (C) 2019 AT&T Intellectual Property. 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.
+# ============LICENSE_END============================================
+# ===================================================================
+#
+###
+
+# build environment
+FROM node:12.4-alpine as build
+WORKDIR /app
+#ENV PATH /app/node_modules/.bin:$PATH
+COPY onap-clamp-frontend/ /app/
+RUN npm install --silent
+RUN npm run build
+
+FROM nginx:1.17.0-alpine
+
+MAINTAINER "The Onap Team"
+LABEL Description="This image contains Clamp frontend"
+
+ARG http_proxy
+ARG https_proxy
+ENV HTTP_PROXY=$http_proxy
+ENV HTTPS_PROXY=$https_proxy
+ENV http_proxy=$HTTP_PROXY
+ENV https_proxy=$HTTPS_PROXY
+
+RUN addgroup onap && adduser -D -G onap clamp
+RUN mkdir /var/log/onap
+RUN chmod a+rwx /var/log/onap
+
+COPY --from=build /app/build /usr/share/nginx/html
+RUN rm /etc/nginx/conf.d/default.conf
+COPY onap-clamp-frontend/nginx/nginx.conf /etc/nginx/conf.d
+EXPOSE 80
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file diff --git a/src/main/docker/frontend/frontend-files.xml b/src/main/docker/frontend/frontend-files.xml new file mode 100644 index 000000000..2610e828e --- /dev/null +++ b/src/main/docker/frontend/frontend-files.xml @@ -0,0 +1,43 @@ +<!-- + ============LICENSE_START======================================================= + ECOMP CLAMP + ================================================================================ + Copyright (C) 2019 AT&T Intellectual Property. 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. + ============LICENSE_END========================================================= + --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd"> + <id>clamp-files</id> + + <formats> + <format>tar.gz</format> + </formats> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <!-- include config files --> + <fileSet> + <excludes> + <exclude>node_modules</exclude> + </excludes> + <directory>${project.basedir}/ui-react</directory> + <outputDirectory>/</outputDirectory> + </fileSet> + </fileSets> + +</assembly> diff --git a/src/main/docker/kibana/Dockerfile.kibana b/src/main/docker/kibana/Dockerfile index 9962fe31a..21e892525 100644 --- a/src/main/docker/kibana/Dockerfile.kibana +++ b/src/main/docker/kibana/Dockerfile @@ -23,8 +23,11 @@ FROM docker.elastic.co/kibana/kibana-oss:6.6.2 +# Search Guard plugin +RUN kibana-plugin install https://repo1.maven.org/maven2/com/floragunn/search-guard-kibana-plugin/6.6.2-18.4/search-guard-kibana-plugin-6.6.2-18.4.zip + MAINTAINER "The Onap Team" -LABEL Description="Kibana image with saved objects loading" +LABEL Description="Kibana image with saved objects loading and Search Guard support" USER root RUN yum install -y python-requests && yum clean all diff --git a/src/main/docker/kibana/backup.py b/src/main/docker/kibana/backup.py index a2dab2222..b1e45181b 100755 --- a/src/main/docker/kibana/backup.py +++ b/src/main/docker/kibana/backup.py @@ -48,7 +48,7 @@ def parse_args(args): ' matching a configuration item that should be written.' ' Files already in the folder that do not match are' ' left as-is.')) - parser.add_argument('-H', '--kibana-host', default='http://localhost:5601', + parser.add_argument('-H', '--kibana-host', default='https://localhost:5601', help='Kibana endpoint.') return parser.parse_args(args) @@ -68,7 +68,7 @@ def main(): # get list of the set of objects we update url = "%s/api/saved_objects/_find" % (args.kibana_host.rstrip("/"),) - saved_objects_req = requests.get(url, + saved_objects_req = requests.get(url, auth=('admin', 'admin'), verify=False, params={'per_page': PER_PAGE,'type':['config','search','dashboard','visualization','index-pattern']}) saved_objects = saved_objects_req.json()['saved_objects'] diff --git a/src/main/docker/kibana/conf/kibana.yml b/src/main/docker/kibana/conf/kibana.yml index 0c4eda9a1..2edcb803f 100644 --- a/src/main/docker/kibana/conf/kibana.yml +++ b/src/main/docker/kibana/conf/kibana.yml @@ -6,4 +6,12 @@ server.host: "0" elasticsearch.hosts: http://elasticsearch:9200 server.ssl.enabled: true server.ssl.key: /usr/share/kibana/config/keystore/org.onap.clamp.key.pem -server.ssl.certificate: /usr/share/kibana/config/keystore/org.onap.clamp.crt.pem
\ No newline at end of file +server.ssl.certificate: /usr/share/kibana/config/keystore/org.onap.clamp.crt.pem + +## Search Guard +# +xpack.security.enabled: false +elasticsearch.username: kibanaserver +elasticsearch.password: kibanaserver + +searchguard.cookie.password: 123567818187654rwrwfsfshdhdhtegdhfzftdhncn
\ No newline at end of file diff --git a/src/main/docker/kibana/restore.py b/src/main/docker/kibana/restore.py index 70f430c57..eee2e6b76 100755 --- a/src/main/docker/kibana/restore.py +++ b/src/main/docker/kibana/restore.py @@ -53,7 +53,7 @@ def parse_args(args): (','.join(OBJECT_TYPES[:-1]), OBJECT_TYPES[-1]) ) ) - parser.add_argument('-H', '--kibana-host', default='http://localhost:5601', + parser.add_argument('-H', '--kibana-host', default='https://localhost:5601', help='Kibana endpoint.') parser.add_argument('-f', '--force', action='store_const', const=True, default=False, @@ -98,7 +98,7 @@ def main(): logger.info('Restoring %s id:%s (overwrite:%s)', obj_type, obj_id, args.force) url = "%s/api/saved_objects/%s/%s" % (args.kibana_host.rstrip("/"), obj_type, obj_id) params = {'overwrite': True} if args.force else {} - post_object_req = requests.post(url, + post_object_req = requests.post(url, auth=('admin', 'admin'), verify=False, headers={'content-type': 'application/json', 'kbn-xsrf': 'True'}, params=params, diff --git a/src/main/docker/logstash/Dockerfile.logstash b/src/main/docker/logstash/Dockerfile index f3075099c..73988dc79 100644 --- a/src/main/docker/logstash/Dockerfile.logstash +++ b/src/main/docker/logstash/Dockerfile @@ -1,17 +1,25 @@ -# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. -# +### +# ============LICENSE_START======================================================= +# ONAP CLAMP +# ================================================================================ +# Copyright (C) 2018 AT&T Intellectual Property. 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 +# 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. - +# ============LICENSE_END============================================ +# =================================================================== +# +### FROM docker.elastic.co/logstash/logstash-oss:6.6.2 MAINTAINER "The Onap Team" diff --git a/src/main/docker/logstash/pipeline/logstash.conf b/src/main/docker/logstash/pipeline/logstash.conf index 6fe9d9691..5c1d47d18 100644 --- a/src/main/docker/logstash/pipeline/logstash.conf +++ b/src/main/docker/logstash/pipeline/logstash.conf @@ -238,6 +238,8 @@ output { elasticsearch { codec => "json" hosts => ["${elasticsearch_base_url}"] + user => "${LOGSTASH_USR}" + password => "${LOGSTASH_PWD}" index => "errors-%{+YYYY.MM.DD}" doc_as_upsert => true } @@ -246,6 +248,8 @@ output { elasticsearch { codec => "json" hosts => ["${elasticsearch_base_url}"] + user => "${LOGSTASH_USR}" + password => "${LOGSTASH_PWD}" document_id => "%{requestID}" index => "events-cl-%{+YYYY.MM.DD}" # creates daily indexes for control loop doc_as_upsert => true @@ -256,6 +260,8 @@ output { elasticsearch { codec => "json" hosts => ["${elasticsearch_base_url}"] + user => "${LOGSTASH_USR}" + password => "${LOGSTASH_PWD}" index => "events-raw-%{+YYYY.MM.DD}" # creates daily indexes doc_as_upsert => true } diff --git a/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java b/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java index 40e547a9c..27a430c7c 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsDictionary.java @@ -28,25 +28,34 @@ import java.util.List; import org.onap.clamp.clds.dao.CldsDao; +import com.google.gson.annotations.Expose; + /** * Represents a CLDS Dictionary. */ public class CldsDictionary { + @Expose private String dictionaryId; + @Expose private String dictionaryName; + + @Expose private String createdBy; + @Expose private String updatedBy; + @Expose private String lastUpdatedDate; + @Expose private List<CldsDictionaryItem> cldsDictionaryItems = new ArrayList<>(); /** * Creates or updates dictionary item for a dictionary in DB. * * @param dictionaryName The dictionary name - * @param cldsDao The CldsDao - * @param userId The user ID + * @param cldsDao The CldsDao + * @param userId The user ID */ public void save(String dictionaryName, CldsDao cldsDao, String userId) { List<CldsDictionary> list = cldsDao.getDictionary(this.getDictionaryId(), dictionaryName); @@ -70,6 +79,7 @@ public class CldsDictionary { /** * Get the dictionary ID. + * * @return the dictionaryId */ public String getDictionaryId() { @@ -78,8 +88,8 @@ public class CldsDictionary { /** * Set the dictionary Id. - * @param dictionaryId - * the dictionaryId to set + * + * @param dictionaryId the dictionaryId to set */ public void setDictionaryId(String dictionaryId) { this.dictionaryId = dictionaryId; @@ -87,6 +97,7 @@ public class CldsDictionary { /** * Get the dictionary name. + * * @return the dictionaryName */ public String getDictionaryName() { @@ -95,8 +106,8 @@ public class CldsDictionary { /** * Set the dictionary name. - * @param dictionaryName - * the dictionaryName to set + * + * @param dictionaryName the dictionaryName to set */ public void setDictionaryName(String dictionaryName) { this.dictionaryName = dictionaryName; @@ -104,6 +115,7 @@ public class CldsDictionary { /** * Get the createdBy info. + * * @return the createdBy */ public String getCreatedBy() { @@ -112,8 +124,8 @@ public class CldsDictionary { /** * Set the createdBy info. - * @param createdBy - * the createdBy to set + * + * @param createdBy the createdBy to set */ public void setCreatedBy(String createdBy) { this.createdBy = createdBy; @@ -121,6 +133,7 @@ public class CldsDictionary { /** * Get the updatedBy info. + * * @return the updatedBy */ public String getUpdatedBy() { @@ -129,8 +142,8 @@ public class CldsDictionary { /** * Set the updatedBy info. - * @param updatedby - * the updatedBy to set + * + * @param updatedby the updatedBy to set */ public void setUpdatedBy(String updatedby) { updatedBy = updatedby; @@ -138,6 +151,7 @@ public class CldsDictionary { /** * Get the last updated date. + * * @return the lastUpdatedDate */ public String getLastUpdatedDate() { @@ -146,8 +160,8 @@ public class CldsDictionary { /** * Set the last updated date. - * @param lastUpdatedDate - * the lastUpdatedDate to set + * + * @param lastUpdatedDate the lastUpdatedDate to set */ public void setLastUpdatedDate(String lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; @@ -155,6 +169,7 @@ public class CldsDictionary { /** * Get all the dictionary items. + * * @return the cldsDictionaryItems */ public List<CldsDictionaryItem> getCldsDictionaryItems() { @@ -163,8 +178,8 @@ public class CldsDictionary { /** * Set the whole dictionary items. - * @param cldsDictionaryItems - * the cldsDictionaryItems to set + * + * @param cldsDictionaryItems the cldsDictionaryItems to set */ public void setCldsDictionaryItems(List<CldsDictionaryItem> cldsDictionaryItems) { this.cldsDictionaryItems = cldsDictionaryItems; diff --git a/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java b/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java index 871fe9037..14a68502e 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsDictionaryItem.java @@ -23,6 +23,8 @@ package org.onap.clamp.clds.model; +import com.google.gson.annotations.Expose; + import java.util.List; import org.onap.clamp.clds.dao.CldsDao; @@ -32,21 +34,31 @@ import org.onap.clamp.clds.dao.CldsDao; */ public class CldsDictionaryItem { + @Expose private String dictElementId; + @Expose private String dictionaryId; + @Expose private String dictElementName; + @Expose private String dictElementShortName; + @Expose private String dictElementDesc; + @Expose private String dictElementType; + @Expose private String createdBy; + @Expose private String updatedBy; + @Expose private String lastUpdatedDate; /** * Save the dictionary item. + * * @param dictionaryName The name of the dictionary - * @param cldsDao The cldsDao - * @param userId The user id + * @param cldsDao The cldsDao + * @param userId The user id */ public void save(String dictionaryName, CldsDao cldsDao, String userId) { // Check if dictionary exists. @@ -55,7 +67,7 @@ public class CldsDictionaryItem { // Dictionary found. We can add or update the dictionary element CldsDictionary cldsDictionary = list.stream().findFirst().get(); List<CldsDictionaryItem> dictionaryItems = cldsDao.getDictionaryElements(dictionaryName, - cldsDictionary.getDictionaryId(), this.getDictElementShortName()); + cldsDictionary.getDictionaryId(), this.getDictElementShortName()); if (dictionaryItems != null && !dictionaryItems.isEmpty()) { CldsDictionaryItem item = dictionaryItems.stream().findFirst().get(); cldsDao.updateDictionaryElements(item.getDictElementId(), this, userId); @@ -71,6 +83,7 @@ public class CldsDictionaryItem { /** * Get the dictionary element id. + * * @return the dictElementId */ public String getDictElementId() { @@ -79,8 +92,8 @@ public class CldsDictionaryItem { /** * Set the dictionary element id. - * @param dictElementId - * the dictElementId to set + * + * @param dictElementId the dictElementId to set */ public void setDictElementId(String dictElementId) { this.dictElementId = dictElementId; @@ -88,6 +101,7 @@ public class CldsDictionaryItem { /** * Get the dictionary id. + * * @return the dictionaryId */ public String getDictionaryId() { @@ -96,8 +110,8 @@ public class CldsDictionaryItem { /** * Set the dictionary id. - * @param dictionaryId - * the dictionaryId to set + * + * @param dictionaryId the dictionaryId to set */ public void setDictionaryId(String dictionaryId) { this.dictionaryId = dictionaryId; @@ -105,6 +119,7 @@ public class CldsDictionaryItem { /** * Get the dictionary name. + * * @return the dictElementName */ public String getDictElementName() { @@ -113,8 +128,8 @@ public class CldsDictionaryItem { /** * Set the dictionary name. - * @param dictElementName - * the dictElementName to set + * + * @param dictElementName the dictElementName to set */ public void setDictElementName(String dictElementName) { this.dictElementName = dictElementName; @@ -122,6 +137,7 @@ public class CldsDictionaryItem { /** * Get the dictionary element short name. + * * @return the dictElementShortName */ public String getDictElementShortName() { @@ -130,8 +146,8 @@ public class CldsDictionaryItem { /** * Set the dictionary element short name. - * @param dictElementShortName - * the dictElementShortName to set + * + * @param dictElementShortName the dictElementShortName to set */ public void setDictElementShortName(String dictElementShortName) { this.dictElementShortName = dictElementShortName; @@ -139,6 +155,7 @@ public class CldsDictionaryItem { /** * Get the dictionary element description. + * * @return the dictElementDesc */ public String getDictElementDesc() { @@ -147,8 +164,8 @@ public class CldsDictionaryItem { /** * Set the dictionary element description. - * @param dictElementDesc - * the dictElementDesc to set + * + * @param dictElementDesc the dictElementDesc to set */ public void setDictElementDesc(String dictElementDesc) { this.dictElementDesc = dictElementDesc; @@ -156,6 +173,7 @@ public class CldsDictionaryItem { /** * Get the dictionary element type. + * * @return the dictElementType */ public String getDictElementType() { @@ -164,8 +182,8 @@ public class CldsDictionaryItem { /** * Set the dictionary element type. - * @param dictElementType - * the dictElementType to set + * + * @param dictElementType the dictElementType to set */ public void setDictElementType(String dictElementType) { this.dictElementType = dictElementType; @@ -173,6 +191,7 @@ public class CldsDictionaryItem { /** * Get the createdBy info. + * * @return the createdBy */ public String getCreatedBy() { @@ -181,8 +200,8 @@ public class CldsDictionaryItem { /** * Set the createdBy info. - * @param createdBy - * the createdBy to set + * + * @param createdBy the createdBy to set */ public void setCreatedBy(String createdBy) { this.createdBy = createdBy; @@ -190,6 +209,7 @@ public class CldsDictionaryItem { /** * Get the updatedBy info. + * * @return the updatedBy */ public String getUpdatedBy() { @@ -198,8 +218,8 @@ public class CldsDictionaryItem { /** * Set the updatedBy info. - * @param updatedby - * the updatedBy to set + * + * @param updatedby the updatedBy to set */ public void setUpdatedBy(String updatedby) { updatedBy = updatedby; @@ -207,6 +227,7 @@ public class CldsDictionaryItem { /** * Get the last updated date. + * * @return the lastUpdatedDate */ public String getLastUpdatedDate() { @@ -215,8 +236,8 @@ public class CldsDictionaryItem { /** * Set the last updated date. - * @param lastUpdatedDate - * the lastUpdatedDate to set + * + * @param lastUpdatedDate the lastUpdatedDate to set */ public void setLastUpdatedDate(String lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; diff --git a/src/main/java/org/onap/clamp/clds/model/CldsInfo.java b/src/main/java/org/onap/clamp/clds/model/CldsInfo.java index a24885f73..f3cf6ed19 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsInfo.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsInfo.java @@ -5,33 +5,42 @@ * Copyright (C) 2017 AT&T Intellectual Property. 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. + * 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 + * + * 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. * ============LICENSE_END============================================ * =================================================================== - * + * */ package org.onap.clamp.clds.model; -public class CldsInfo { +import com.google.gson.annotations.Expose; +public class CldsInfo { + @Expose private String userName; + @Expose private String cldsVersion; + @Expose private boolean permissionReadCl; + @Expose private boolean permissionUpdateCl; + @Expose private boolean permissionReadTemplate; + @Expose private boolean permissionUpdateTemplate; + @Expose private boolean permissionReadTosca; + @Expose private boolean permissionUpdateTosca; public String getUserName() { diff --git a/src/main/java/org/onap/clamp/clds/model/CldsModel.java b/src/main/java/org/onap/clamp/clds/model/CldsModel.java index 223d38929..4d2ca89a4 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsModel.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsModel.java @@ -23,8 +23,7 @@ package org.onap.clamp.clds.model; -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; +import com.google.gson.annotations.Expose; import java.util.ArrayList; import java.util.List; @@ -43,7 +42,6 @@ import org.onap.clamp.clds.model.status.StatusHandlerImpl; */ public class CldsModel { - private static final EELFLogger logger = EELFManager.getInstance().getLogger(CldsModel.class); private static final int UUID_LENGTH = 36; /** * The constant STATUS_DESIGN. @@ -73,29 +71,50 @@ public class CldsModel { * The constant STATUS_UNKNOWN. */ public static final String STATUS_UNKNOWN = "UNKNOWN"; + + @Expose private String id; + @Expose private String templateId; + @Expose private String templateName; + @Expose private String name; + @Expose private String controlNamePrefix; + @Expose private String controlNameUuid; + @Expose private String bpmnText; + @Expose private String propText; + @Expose private String imageText; + @Expose private String docText; + @Expose private String blueprintText; + @Expose private CldsEvent event; + @Expose private String status; + @Expose private List<String> permittedActionCd; + @Expose private List<CldsModelInstance> cldsModelInstanceList; // This is a transient value used to return the failure message to UI + @Expose private String errorMessageForUi; /** * The service type Id received from DCAE by querying it. */ + @Expose private String typeId; + @Expose private String typeName; + @Expose private String deploymentId; + @Expose private String deploymentStatusUrl; // Set default handlers but this can be changed if needed. @@ -155,7 +174,7 @@ public class CldsModel { boolean canCall = false; /* Below checks the clds event is submit/resubmit/distribute */ if (event.isActionCd(CldsEvent.ACTION_SUBMIT) || event.isActionCd(CldsEvent.ACTION_RESUBMIT) - || event.isActionCd(CldsEvent.ACTION_DISTRIBUTE) || event.isActionCd(CldsEvent.ACTION_SUBMITDCAE)) { + || event.isActionCd(CldsEvent.ACTION_DISTRIBUTE) || event.isActionCd(CldsEvent.ACTION_SUBMITDCAE)) { canCall = true; } return canCall; @@ -202,9 +221,9 @@ public class CldsModel { public void validateAction(String requestedActionCd) { determinePermittedActionCd(); if (!permittedActionCd.contains(requestedActionCd)) { - throw new IllegalArgumentException( - "Invalid requestedActionCd: " + requestedActionCd + ". Given current actionCd: " - + actionsHandler.getCurrentActionCd(event) + ", the permittedActionCd: " + permittedActionCd); + throw new IllegalArgumentException("Invalid requestedActionCd: " + requestedActionCd + + ". Given current actionCd: " + actionsHandler.getCurrentActionCd(event) + + ", the permittedActionCd: " + permittedActionCd); } } @@ -220,8 +239,8 @@ public class CldsModel { public static CldsModel createUsingControlName(String fullControlName) { if (fullControlName == null || fullControlName.length() < UUID_LENGTH) { throw new BadRequestException( - "closed loop id / control name length, " + (fullControlName != null ? fullControlName.length() : 0) - + ", less than the minimum of: " + UUID_LENGTH); + "closed loop id / control name length, " + (fullControlName != null ? fullControlName.length() : 0) + + ", less than the minimum of: " + UUID_LENGTH); } CldsModel model = new CldsModel(); model.setControlNamePrefix(fullControlName.substring(0, fullControlName.length() - UUID_LENGTH)); @@ -251,11 +270,11 @@ public class CldsModel { CldsModel cldsModel = createUsingControlName(controlName); cldsModel = cldsDao.getModelByUuid(cldsModel.getControlNameUuid()); cldsModel.determineStatus(); - if (dcaeEvent.getCldsActionCd().equals(CldsEvent.ACTION_UNDEPLOY) - || (dcaeEvent.getCldsActionCd().equals(CldsEvent.ACTION_DEPLOY) + if (dcaeEvent.getCldsActionCd().equals(CldsEvent.ACTION_UNDEPLOY) || (dcaeEvent.getCldsActionCd() + .equals(CldsEvent.ACTION_DEPLOY) && (cldsModel.getStatus().equals(STATUS_DISTRIBUTED) || cldsModel.getStatus().equals(STATUS_DESIGN)))) { CldsEvent.insEvent(cldsDao, dcaeEvent.getControlName(), userid, dcaeEvent.getCldsActionCd(), - CldsEvent.ACTION_STATE_RECEIVED, null); + CldsEvent.ACTION_STATE_RECEIVED, null); } cldsDao.insModelInstance(cldsModel, dcaeEvent.getInstances()); return cldsModel; diff --git a/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java b/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java index 45194deca..8777ab5c9 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsServiceData.java @@ -18,7 +18,7 @@ * limitations under the License. * ============LICENSE_END============================================ * =================================================================== - * + * */ package org.onap.clamp.clds.model; @@ -77,8 +77,8 @@ public class CldsServiceData implements Serializable { } /** - * Filter out any VFs that the user is not authorized for. Use the - * CldsService to determine if the user is authorized for a VF. + * Filter out any VFs that the user is not authorized for. Use the CldsService + * to determine if the user is authorized for a VF. * * @param svc The clds service */ diff --git a/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java b/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java index 8f3408375..b1247801b 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsTemplate.java @@ -18,11 +18,13 @@ * limitations under the License. * ============LICENSE_END============================================ * =================================================================== - * + * */ package org.onap.clamp.clds.model; +import com.google.gson.annotations.Expose; + import javax.ws.rs.NotFoundException; import org.onap.clamp.clds.dao.CldsDao; @@ -32,35 +34,48 @@ import org.onap.clamp.clds.dao.CldsDao; */ public class CldsTemplate { - public static final String STATUS_DESIGN = "DESIGN"; - public static final String STATUS_ACTIVE = "ACTIVE"; - public static final String STATUS_STOPPED = "STOPPED"; + public static final String STATUS_DESIGN = "DESIGN"; + public static final String STATUS_ACTIVE = "ACTIVE"; + public static final String STATUS_STOPPED = "STOPPED"; public static final String STATUS_DELETING = "DELETING"; // manual intervention required - public static final String STATUS_ERROR = "ERROR"; - public static final String STATUS_UNKNOWN = "UNKNOWN"; - - private String id; - private String name; - private String controlNamePrefix; - private String controlNameUuid; - private String bpmnId; - private String bpmnUserid; - private String bpmnText; - private String imageId; - private String imageUserid; - private String imageText; - private String propId; - private String propUserid; - private String propText; - - private boolean userAuthorizedToUpdate; + public static final String STATUS_ERROR = "ERROR"; + public static final String STATUS_UNKNOWN = "UNKNOWN"; + + @Expose + private String id; + @Expose + private String name; + @Expose + private String controlNamePrefix; + @Expose + private String controlNameUuid; + @Expose + private String bpmnId; + @Expose + private String bpmnUserid; + @Expose + private String bpmnText; + @Expose + private String imageId; + @Expose + private String imageUserid; + @Expose + private String imageText; + @Expose + private String propId; + @Expose + private String propUserid; + @Expose + private String propText; + @Expose + private boolean userAuthorizedToUpdate; /** * Save template to DB. * * @param cldsDao The cldsDao - * @param userid The user Id + * @param userid The user Id */ public void save(CldsDao cldsDao, String userid) { cldsDao.setTemplate(this, userid); @@ -69,10 +84,10 @@ public class CldsTemplate { /** * Retrieve from DB. * - * @param cldsDao The cldsDao - * @param name The template name to retrieve - * @param okIfNotFound - * The flag indicating whether exception will be returned in case nothing is found + * @param cldsDao The cldsDao + * @param name The template name to retrieve + * @param okIfNotFound The flag indicating whether exception will be returned in + * case nothing is found * @return Clds template from DB */ public static CldsTemplate retrieve(CldsDao cldsDao, String name, boolean okIfNotFound) { diff --git a/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java b/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java index 961e1e328..5ae13d20a 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsToscaModel.java @@ -23,6 +23,8 @@ package org.onap.clamp.clds.model; +import com.google.gson.annotations.Expose; + import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -33,8 +35,11 @@ import org.onap.clamp.clds.tosca.ToscaYamlToJsonConvertor; public class CldsToscaModel extends CldsToscaModelRevision { + @Expose private String id; + @Expose private String policyType; + @Expose private String toscaModelName; /** @@ -47,7 +52,7 @@ public class CldsToscaModel extends CldsToscaModelRevision { * Creates or updates Tosca Model to DB. * * @param cldsDao The cldsDao - * @param userId The user Id + * @param userId The user Id */ public CldsToscaModel save(CldsDao cldsDao, ClampProperties refProp, PolicyClient policyClient, String userId) { CldsToscaModel cldsToscaModel = null; @@ -85,6 +90,7 @@ public class CldsToscaModel extends CldsToscaModelRevision { /** * Get the Id. + * * @return the id */ public String getId() { @@ -93,8 +99,8 @@ public class CldsToscaModel extends CldsToscaModelRevision { /** * Set the id. - * @param id - * the id to set + * + * @param id the id to set */ public void setId(String id) { this.id = id; @@ -102,6 +108,7 @@ public class CldsToscaModel extends CldsToscaModelRevision { /** * Get the policy type. + * * @return the policyType */ public String getPolicyType() { @@ -110,8 +117,8 @@ public class CldsToscaModel extends CldsToscaModelRevision { /** * Set the policy type. - * @param policyType - * the policyType to set + * + * @param policyType the policyType to set */ public void setPolicyType(String policyType) { this.policyType = policyType; @@ -119,6 +126,7 @@ public class CldsToscaModel extends CldsToscaModelRevision { /** * Get the tosca model name. + * * @return the toscaModelName */ public String getToscaModelName() { @@ -127,8 +135,8 @@ public class CldsToscaModel extends CldsToscaModelRevision { /** * Set the tosca model name. - * @param toscaModelName - * the toscaModelName to set + * + * @param toscaModelName the toscaModelName to set */ public void setToscaModelName(String toscaModelName) { this.toscaModelName = toscaModelName; diff --git a/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java b/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java index 9448cefcd..034fc28ce 100644 --- a/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java +++ b/src/main/java/org/onap/clamp/clds/model/CldsVfcData.java @@ -5,20 +5,20 @@ * Copyright (C) 2017 AT&T Intellectual Property. 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. + * 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 + * + * 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. * ============LICENSE_END============================================ * =================================================================== - * + * */ package org.onap.clamp.clds.model; @@ -28,9 +28,9 @@ import java.util.List; public class CldsVfcData implements Serializable { - private static final long serialVersionUID = 406993878174183557L; - private String vfcName; - private String vfcInvariantResourceUuid; + private static final long serialVersionUID = 406993878174183557L; + private String vfcName; + private String vfcInvariantResourceUuid; private List<CldsAlarmCondition> cldsAlarmConditions; public String getVfcName() { diff --git a/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java b/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java index a6ad10b72..9126f9abf 100644 --- a/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java +++ b/src/main/java/org/onap/clamp/clds/model/DcaeEvent.java @@ -23,6 +23,8 @@ package org.onap.clamp.clds.model; +import com.google.gson.annotations.Expose; + import java.util.List; import javax.ws.rs.BadRequestException; @@ -38,10 +40,15 @@ public class DcaeEvent { public static final String EVENT_UNDEPLOYMENT = "undeployment"; public static final String ARTIFACT_NAME_SUFFIX = ".yml"; + @Expose private String event; + @Expose private String serviceUuid; + @Expose private String resourceUuid; + @Expose private String artifactName; // controlName.yml + @Expose private List<CldsModelInstance> instances; /** @@ -83,12 +90,13 @@ public class DcaeEvent { return artifactName.substring(0, artifactName.length() - ARTIFACT_NAME_SUFFIX.length()); } else { throw new BadRequestException("artifactName value not valid (expecting it to end with " - + ARTIFACT_NAME_SUFFIX + "): " + artifactName); + + ARTIFACT_NAME_SUFFIX + "): " + artifactName); } } /** * Get the event. + * * @return the event */ public String getEvent() { @@ -97,8 +105,8 @@ public class DcaeEvent { /** * Set the event. - * @param event - * the event to set + * + * @param event the event to set */ public void setEvent(String event) { this.event = event; @@ -106,6 +114,7 @@ public class DcaeEvent { /** * Get the serviceUUID. + * * @return the serviceUUID */ public String getServiceUUID() { @@ -114,8 +123,8 @@ public class DcaeEvent { /** * Set the serviceUUID. - * @param serviceUUID - * the serviceUUID to set + * + * @param serviceUUID the serviceUUID to set */ public void setServiceUUID(String serviceUuid) { this.serviceUuid = serviceUuid; @@ -123,6 +132,7 @@ public class DcaeEvent { /** * Get the resourceUUID. + * * @return the resourceUUID */ public String getResourceUUID() { @@ -131,8 +141,8 @@ public class DcaeEvent { /** * Set the resourceUUID. - * @param resourceUUID - * the resourceUUID to set + * + * @param resourceUUID the resourceUUID to set */ public void setResourceUUID(String resourceUuid) { this.resourceUuid = resourceUuid; @@ -140,6 +150,7 @@ public class DcaeEvent { /** * Get the artifact name. + * * @return the artifactName */ public String getArtifactName() { @@ -148,8 +159,8 @@ public class DcaeEvent { /** * Set the artifact name. - * @param artifactName - * the artifactName to set + * + * @param artifactName the artifactName to set */ public void setArtifactName(String artifactName) { this.artifactName = artifactName; @@ -157,6 +168,7 @@ public class DcaeEvent { /** * Get the list of instances. + * * @return The list of model instances */ public List<CldsModelInstance> getInstances() { @@ -165,6 +177,7 @@ public class DcaeEvent { /** * Set the list of model instances. + * * @param instances The list of model instances to set */ public void setInstances(List<CldsModelInstance> instances) { diff --git a/src/main/java/org/onap/clamp/clds/model/ValueItem.java b/src/main/java/org/onap/clamp/clds/model/ValueItem.java index 74b369f79..42323908c 100644 --- a/src/main/java/org/onap/clamp/clds/model/ValueItem.java +++ b/src/main/java/org/onap/clamp/clds/model/ValueItem.java @@ -5,28 +5,31 @@ * Copyright (C) 2017 AT&T Intellectual Property. 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. + * 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 + * + * 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. * ============LICENSE_END============================================ * =================================================================== - * + * */ package org.onap.clamp.clds.model; +import com.google.gson.annotations.Expose; + /** * ValueItem used for value lists. */ public class ValueItem { + @Expose private String value; /** @@ -43,6 +46,7 @@ public class ValueItem { /** * Get the value. + * * @return the value */ public String getValue() { @@ -51,8 +55,8 @@ public class ValueItem { /** * Set the value. - * @param value - * the value to set + * + * @param value the value to set */ public void setValue(String value) { this.value = value; diff --git a/src/main/java/org/onap/clamp/loop/LoopController.java b/src/main/java/org/onap/clamp/loop/LoopController.java index b862780de..7d41e489a 100644 --- a/src/main/java/org/onap/clamp/loop/LoopController.java +++ b/src/main/java/org/onap/clamp/loop/LoopController.java @@ -116,12 +116,13 @@ public class LoopController { /** * Get the SVG representation of the loop - * + * * @param loopName * The loop name * @return The SVG representation */ public String getSVGRepresentation(String loopName) { - return loopService.getLoop(loopName).getSvgRepresentation(); + Loop loop = loopService.getLoop(loopName); + return loop != null ? loop.getSvgRepresentation() : null; } } diff --git a/src/main/resources/boot-message.txt b/src/main/resources/boot-message.txt index 92e4ab029..46b0a6faa 100644 --- a/src/main/resources/boot-message.txt +++ b/src/main/resources/boot-message.txt @@ -1,14 +1,15 @@ - _____ _ _ __ ____ ____ __ __ ____ __ ____ _ _ -( _ )( \( ) /__\ ( _ \ ( _ \( )( )( _ \( ) (_ _)( \( ) - )(_)( ) ( /(__)\ )___/ )(_) ))(__)( ) _ < )(__ _)(_ ) ( -(_____)(_)\_)(__)(__)(__) (____/(______)(____/(____)(____)(_)\_) - ___ __ __ __ __ ____ - / __)( ) /__\ ( \/ )( _ \ - ( (__ )(__ /(__)\ ) ( )___/ - \___)(____)(__)(__)(_/\/\_)(__) - + ▐ ▄ ▄▄▄· ▄▄▄· ▄▄▄ .▄▄▌ ▄▄▄· ▄▄▌ ▄▄▄▄▄ +▪ •█▌▐█▐█ ▀█ ▐█ ▄█ ▀▄.▀·██• ▐█ ▀█ ██• •██ ▪ + ▄█▀▄ ▐█▐▐▌▄█▀▀█ ██▀· ▐▀▀▪▄██▪ ▄█▀▀█ ██▪ ▐█.▪ ▄█▀▄ +▐█▌.▐▌██▐█▌▐█ ▪▐▌▐█▪·• ▐█▄▄▌▐█▌▐▌ ▐█ ▪▐▌▐█▌▐▌▐█▌·▐█▌.▐▌ + ▀█▄▀▪▀▀ █▪ ▀ ▀ .▀ ▀▀▀ .▀▀▀ ▀ ▀ .▀▀▀ ▀▀▀ ▀█▄▀▪ + ▄▄· ▄▄▌ ▄▄▄· • ▌ ▄ ·. ▄▄▄· + ▐█ ▌▪██• ▐█ ▀█ ·██ ▐███▪▐█ ▄█ + ██ ▄▄██▪ ▄█▀▀█ ▐█ ▌▐▌▐█· ██▀· + ▐███▌▐█▌▐▌▐█ ▪▐▌██ ██▌▐█▌▐█▪·• + ·▀▀▀ .▀▀▀ ▀ ▀ ▀▀ █▪▀▀▀.▀ :: Starting ::
\ No newline at end of file |