<<

Masaryk University Faculty of Informatics

Development of Modern User Interfaces in Framework

Master’s Thesis Bc. Martin Hamerník

Brno, Fall 2020

This is where a copy of the official signed thesis assignment and a copy of the Statement of anAuthor is located in the printed version of the document.

Declaration

Hereby I declare that this paper is my original authorial work, which I have worked out on my own. All sources, references, and literature used or excerpted during elaboration of this work are properly cited and listed in complete reference to the due source.

Bc. Martin Hamerník

Advisor: RNDr. Daniel Tovarňák, Ph.D.

i

Acknowledgements

My thanks belong to my advisor RNDr. Daniel Tovarňák, Ph.D for leading this thesis, valu- able consultations, and technological leadership. I would also like to thank my family and friends for their endless support.

iii Abstract

This thesis aims to analyze the current state of development of web user interfaces in gen- eral and in the CSIRT-MU environment, design and apply modern architectural approaches based on microservices, and create a set of supportive tools to improve quality and speed of development of new user interfaces. To achieve this goal, the author designs and imple- ments a framework for Angular applications. The framework consists of libraries provid- ing a unified pre-defined layout, a set of base components for the development ofcomplex user interfaces, and interfaces for authentication and authorization. In addition to the afore- mentioned capabilities, the framework proposes several architectural patterns suitable for developing complex Angular applications. A part of this thesis is a practical application of the proposed architectural patterns and integration of the framework into the KYPO Cyber Range Platform and Security Dashboard web applications.

iv Keywords frontend, user interface, angular, kypo, cybersecurity, csirt-mu, software architecture, soft- ware engineering

v

Contents

1 Introduction 1

2 Modern Frontend Development 3 2.1 The Basics ...... 3 2.2 Types of Web Applications ...... 4 2.2.1 Traditional Web Applications ...... 4 2.2.2 Single Page Applications ...... 5 2.2.3 Progressive Web Applications ...... 5 2.3 Rendering Approaches ...... 6 2.3.1 Static Server-side Rendering ...... 7 2.3.2 Server-side Rendering ...... 8 2.3.3 Client-side Rendering ...... 8 2.3.4 Combining the Approaches ...... 9 2.4 Modern JavaScript Frameworks ...... 11 2.4.1 Angular ...... 11 2.4.2 React ...... 17 2.4.3 Vue ...... 19 2.5 Summary ...... 20

3 Environment 21 3.1 Organizational Context ...... 21 3.1.1 CSIRT-MU ...... 21 3.1.2 Projects ...... 21 3.2 Author’s Work ...... 23 3.3 Technological Environment ...... 23 3.3.1 Architecture of KYPO ...... 23 3.3.2 Architecture of Security Dashboard ...... 26 3.3.3 Development Technologies ...... 27 3.4 Motivation for Development of Unified Framework ...... 29 3.5 Functional Requirements of the Framework ...... 30 3.6 Non-functional Requirements of the Framework ...... 30

4 Analysis and Design 31 4.1 Micro Frontends ...... 31 4.2 Micro Frontends in Angular ...... 34 4.3 Angular Libraries ...... 35 4.4 Architecture of an Agenda ...... 37 4.5 Smart-dumb Components ...... 39

vii 4.6 Application State ...... 40 4.7 Anatomy of CSIRT-MU Applications ...... 41 4.8 API Code Generation ...... 43 4.9 Build Once, Deploy Many ...... 44

5 Sentinel Framework 47 5.1 Sentinel Common ...... 47 5.2 Sentinel Layout ...... 48 5.2.1 Breadcrumbs ...... 49 5.2.2 Notifications ...... 50 5.2.3 Schematics ...... 51 5.3 Sentinel Auth ...... 51 5.4 Sentinel Components ...... 53 5.4.1 Bug Reporting ...... 54 5.4.2 Control Panel ...... 55 5.4.3 Confirmation Dialog ...... 55 5.4.4 Error Directive ...... 55 5.4.5 Infinite Scroll ...... 55 5.4.6 Infinite List ...... 56 5.4.7 Code Viewer ...... 56 5.4.8 Loading Component ...... 56 5.4.9 Markdown Editor ...... 56 5.4.10 Resource Selector ...... 57 5.4.11 Stepper ...... 57 5.4.12 Free Form ...... 57 5.4.13 Table ...... 59 5.4.14 Tour Guide ...... 60 5.4.15 User Assignment ...... 60 5.5 Agendas ...... 62 5.6 Dynamic Environments ...... 64

6 Application of the Sentinel Framework 67 6.1 The KYPO GUI ...... 67 6.2 The Security Dashboard GUI ...... 70

7 Testing 73 7.1 General Principles ...... 73 7.2 Testing in Angular ...... 74 7.2.1 Karma ...... 74 7.2.2 Unit and Integration Testing ...... 75 viii 7.2.3 End-to-end Testing ...... 77

8 Conclusion 79 8.1 Future Work ...... 79

A Sentinel Framework 81

B KYPO CRP 83

Bibliography 85

ix

1 Introduction

Over the last couple of years, a rise in the popularity of complex full-fledged web appli- cations can be observed. In our minds, writing a document, media playback, or even pro- gramming is no longer an activity strictly associated with desktop applications. Increasing performance of desktop and mobile devices, expansion of high-speed internet connection availability, and capabilities of modern web browsers allow us to conduct most of these activities directly in the browser. The increase of capabilities and amount of features, present in today’s web applications, projects into the complexity and costs of frontend development. Today’s frontend develop- ment often handles complex business and rendering logic. The goal of this thesis is to analyze the current state of development of user interfaces in the context of CSIRT-MU organization, design and apply architectural patterns, and pro- vide essential building blocks such as base components, to increase the efficiency of devel- opment of new user interfaces. After this introductory chapter, the thesis discusses the current state of modern fron- tend development in chapter 2. The chapter briefly introduces languages and standards of today’s web development, continued by a distinction of types of web applications and rendering approaches, and introduction of modern JavaScript frameworks. Chapter 3 describes the environment in which the thesis originated, puts the work in the context of CSIRT-MU from an organizational and technological point of view. It also states requirements on the architectural patterns and development framework. Chapter 4 analyzes modern architectural and design patterns used in frontend devel- opment and its suitability for the CSIRT-MU environment. It starts with the high-level ar- chitecture of web applications in the context of complex systems and continues to analyze patterns on lower levels, covering patterns for the decomposition of an application, state management, and best practices for component architecture. A framework developed to provide an implementation of the proposed architectural patterns as well as essential reusable building blocks for web applications is described in chapter 5. Next, chapter 6 demonstrates the application of the patterns and shared building blocks in two existing projects. Chapter 7 discusses approaches and possibilities of testing in frontend environment and specifically in the Angular framework. It also introduces and compares several libraries for testing. The last chapter summarizes the thesis, states its practical outputs, and proposes possi- bilities of further improvements and extension.

1

2 Modern Frontend Development

Web development is a dynamic and ever-changing environment with a large and active open-source community. Standards, languages, and tooling are continually evolving. This chapter briefly introduces web development basics in section 2.1, followed by an overview of types of web applications in section 2.2. Section 2.3 covers current approaches application rendering and associated challenges related to performance, search engine visibility, and user experience. The last section 2.4 introduces Angular, React, and Vue, the three most popular JavaScript frameworks for building modern web applications.

2.1 The Basics

HTML - Hyper Text Markup Language

HTML is still the basic building block of the web. It gives structure and meaning to a simple text, in a way understood by a . The HTML consists of elements. Attributes can modify the elements, and user interactions can be detected by listening to events [1]. When rendering a website, the web browser fetches an HTML file from a server, parses the HTML code, and constructs a hierarchical tree structure called Document Object Model (DOM) [2].

CSS - Cascading Style Sheets

CSS is a style sheet language describing the presentation and layout of web documents. CSS consists of rules and selectors. Numerous predefined rules supported by web browsers can be used to alter the presentation of a DOM element. The element which should be affected by the rules is determined by matching a pattern described by a selector [3].Asthe DOM is being built, the web browser parses the CSS and creates a render tree, containing information on how should the HTML and its styles be displayed in a browser window [4].

JavaScript

JavaScript is an interpreted programming language, mostly known and used for running scripts in web browsers, but also popular on the server-side. It is a multi-paradigm lan- guage, supporting object-oriented, declarative, imperative, and event-driven programming styles. A web browser’s can run a JavaScript code. Earlier, the most common use case was to run small scripts, altering the DOM or styles, or reacting to user behaviour and events. Nowadays, JavaScript is often used to generate the whole web application either on client-side or on the server-side [5].

3 2. Modern Frontend Development

ECMAScript

ECMAScript is a general-purpose programming language, standardized and developed by (European Computer Manufacturers Association). The standard ensures interoperability and behaviour of specific language features across a range ofweb browsers. JavaScript is based on the standard. A technique called transpilation is often used to adopt new features of the language, and at the same time, preserve backward compat- ibility with older versions of web browsers. Transpilation is a process of compiling code written in modern standard to older versions [6].

TypeScript

TypeScript is an open-source programming language developed by Microsoft. It extends JavaScript by adding static type definitions, interfaces, modules, and classes while support- ing type inference. It helps to write safer, more declarative code, and it is supported by most of the modern IDEs. The TypeScript strictness in type checking is adjustable and suitable for various types of projects (strict TypeScript only project, or projects in which TypeScript is incrementally introduced to an existing JavaScript codebase). At the build- time, the TypeScript code is compiled to a regular JavaScript, ensuring compatibility with all browsers and servers already capable of running a JavaScript code [7].

2.2 Types of Web Applications

Before delving into rendering approaches and contemporary JavaScript frameworks, it is appropriate to cover the types of web applications and differences between them. Gener- ally, we can split most of the current web applications into three categories based on their characteristics.

2.2.1 Traditional Web Applications

What is being considered a traditional web application in the context of this thesis is a web application loading every page from the server separately. When a user raises a navigation event to a subsequent page, a browser gets the page from a server and completely reloads the web application. Often, such applications are static or rendered on the server, consisting predominantly of HTML and CSS. In these cases, JavaScript is typically used for smaller manipulations of the DOM and for adding a dynamic aspect to a static page. Because only data for the active page are fetched from the server, and also because the relatively low amount of JavaScript code makes the application less demanding on client de- vices, it can be said, that in general, such web applications perform better and have smaller

4 2. Modern Frontend Development bundle size. They also perform better in terms of SEO 1 because the static content is more digestible for most of the current web crawlers. On the other hand, in comparison to single-page applications, the user experience of tra- ditional web applications is usually inferior. Refreshing the page on each navigational event is disturbing and breaks the user’s immersion in the application. Traditional web applica- tions can no longer keep up with the trend of moving complex desktop-like applications to web browsers.

2.2.2 Single Page Applications

A single-page application (SPA) is a more modern approach to building a web application. Contrary to the traditional approach, it starts with loading the whole application from a server all at once. SPAs are usually served as a single HTML file, containing only basic HTML structure, importing the application bundled in JavaScript file. The JavaScript code generates the HTML and CSS code directly in the browser. The application then handles routing and loading of pages on the client-side, by trans- forming the DOM based on the currently active route and state of the application. It allows for faster interactions, transitions, animations, and rich user interactions, making the web application feel native [9]. SPAs rely heavily on JavaScript and JavaScript frameworks. They are generally more demanding on the client device’s CPU (problematic mainly on low-end mobile devices) and quality of the internet connection. Modern web applications usually ship JavaScript code containing the whole SPA framework, UI libraries, libraries for state management, HTTP communication, and occasionally other popular utility libraries. According to the report made by HttpArchive [10], the median size of JavaScript code loaded by a browser on each website was around 420 KB in 2019. Code-splitting and techniques such as tree shaking can reduce the bundle size. Then, the application can be split into multiple bundles [11]. The largest bundle, containing the framework, loads first, together with the application’s core and the first displayed page. Loading the other bundles is deferred to the timewhen they are needed. This design approach is called lazy loading [12].

2.2.3 Progressive Web Applications

Unlike the previous types of web applications, the term progressive web application (PWA) is inclusive. It means that applications can be both SPAand PWAat the same time. PWAsare not a standard, but a design pattern enlisting several attributes, features, and requirements [13].

1. Search Engine Optimization (SEO) is a process of transforming the code of a website to be readable by web crawlers in order to increase the visibility of the website in search engines. Better ranking and visibility in search engine results increase the traffic of the website [8].

5 2. Modern Frontend Development

There are three main requirements a web application should meet to be called progres- sive.

• Be served over HTTPS. • Contain at least one service worker. • Contain at least one manifest file.

Being served over a secured network is a requirement closely connected to the second requirement. Not only it is a best practice, but being served over a secure network is also a condition under which browser unlocks parts of its API, heavily used by PWAs,for example, geolocation, camera, or service workers [14]. A service worker is a JavaScript code able to run in the browser outside the scope of a web application. Service workers include features such as web push notifications, caching, or synchronizing actions in the background. In general, service workers allow developers to provide a user experience closer to fast native applications, which do not need to be online all the time [15]. A manifest is a JSON file describing an application in a format readable by most ofthe modern browsers. The file contains important information about the application, includ- ing name, description, icon, and author. It allows the web application to be installed on a mobile device without an application store. After installing, the application can be run in a container, native for the specific platform, erasing a difference between a web application and a native application.

2.3 Rendering Approaches

There are several crucial architectural choices prior to building a web application. One of the most important choices, profoundly influencing how an application will look and how it will perform, is an approach to the rendering of an application. In general, the contempo- rary approaches to rendering can be split into categories based on how the page is rendered (statically or dynamically) and where it is rendered (server-side or client-side). Impact on performance is one of the attributes of rendering approach subjected to exami- nation in the following sections. The subsequent sections compare the performance impacts of the rendering approaches by the following metrics [16]:

• Time To First Byte (TTFB) - The duration between sending an HTTP request and the first byte of the response being received. • First Paint (FP) - Marks a point when does a user see the first pixel. • First Contentful Paint (FCP) - When does the first actual content of the page appear (navigation, images, etc.).

6 2. Modern Frontend Development

1. Client requests data for an URL

3. Server responds with an HTML file

2. Server retrieves the file from its file system

Figure 2.1: Client and server interaction in static server-side rendering.

• First Meaningful Paint (FMP) - At this point, the primary content of the page is visible.

• Time To Interactive (TTI) - How long does it take until the page is fully interactive (browser events registered).

2.3.1 Static Server-side Rendering

Statically server-side rendered (SSSR) web pages [17, 18] do not change their content based on the context in which they are accessed. They render at the build time. Hence, all of the content needs to be known at the build time. This approach can be very fast and performant, but it is suitable only for a limited subset of use cases. The most considerable advantage of the static rendering is that in general, it achieves a better TTFB because the server does not have to run any code to generate the content of the web page with each request. The server already has the web page generated and merely sends it to the client. Other metrics such as FP, FCP and TTI can also be fast, assuming that the page contains a limited amount JavaScript code. The largest limitation of the static rendering approach is that the content of pages needs to be known and generated upfront for each possible URL. It might be a reasonable limi- tation for simple use cases, for example, online business presentation, documentation, or blogs. However, for anything more complex, with features such as user-specific content or dynamically changing content, it is very hard or even impossible to know all the possible combinations of the web page content at the build time.

7 2. Modern Frontend Development

1. Client requests data for an URL

3. Server responds with an HTML file

2. Server generates content of a response

Figure 2.2: Client and server interaction in dynamic server-side rendering.

2.3.2 Server-side Rendering

By generating the web page on the server at a time when a request is received, server-side rendering (SSR) can produce dynamic content based on the URL and other parameters. This approach has a higher TTFB because the web page is generated after the request is received, so compared to the static approach, it needs more time before the response can be sent back to the client. For FP, FCP, and TTI, it depends on the amount of JavaScript on the web page. Server-side rendering is suitable for use cases where content needs to be dynamic, and performance on low-end devices is more critical than the native-like UI of client-side ren- dered SPAs. It is also important to consider, that unlike the static rendering (where render- ing is a one-time event) or client-side rendering, with server-side rendering the rendering is done with each request, making it harder for servers to cope with larger loads without any form of smart optimization such as caching.

2.3.3 Client-side Rendering

Client-side rendering (CSR) [18, 19] is a relatively new rendering technique. In this ap- proach, the server usually sends only one simple HTML file and multiple JavaScript files.

8 2. Modern Frontend Development

1. Client requests data for an URL

3. Server responds with HTML and JavaScript files

2. Server retrieves HTML 4. The application is and JavaScript file generated in the browser from its file system

Figure 2.3: Client and server interaction in client-side rendering.

On the server-side, the effort to serve those files is similar to the static rendering approach, making the TTFB quite fast. The client receives the HTML file and runs the JavaScript code, usually containing a whole framework for generating the content, handling routing, managing the application’s state, and HTTP communication. The application itself comes after. The amount of JavaScript code rises proportionately to the size of the application. Values of FP, FCP or TTI, are de- rived from the speed of the internet connection (TTFB is fast, but the size of the JavaScript code is also larger than a static HTML) and more importantly, from the performance of a client’s device. On slower devices, the difference between FP or FCP and TTI can be high, because parts of the layout or shared application shell can be served statically (and they often are), mak- ing them appear quite fast. Then, the device needs to do the rendering of the content itself, which is blocking the main thread of the browser, therefore handling of the browser events. It causes the application to appear to be nearly rendered, but it takes a considerable amount of time (seconds or even tens of seconds on low-end mobile devices) before it is interactive and reacts to events (scrolling, clicking, etc.) [20].

2.3.4 Combining the Approaches

In practice, the SSR and CSR are often combined, to balance the characteristics of both approaches. Such applications are called universal or isomorphic, for their ability to be rendered on both sides [21].

9 2. Modern Frontend Development

4. Static version of the application is displayed 1. Client requests data for an URL

3. Server responds with static and dynamic version of the application

2. Server retrieves HTML and JavaScript file 5. Full version is booted and from its file system takes over the control

Figure 2.4: Client and server interaction in server-side rendering combined with client-side rendering.

In the case of universal applications, the client is served a server-side generated skele- ton of the application, containing the page’s core with layout, navigation, and initial content that grasps the user’s attention. The initial application has limited functionality, mostly lack- ing the possibility of navigating to other pages or interacting with the UI [22]. Meanwhile, in the background, the client receives a full-fledged version of the application (rendered on the client-side), replacing the initial server-generated version. This technique is called rehydration [23]. While this approach can significantly improve FP and FCP, it harms the TTI because not only the client’s device needs to handle displaying the first version of the application, it also needs to render the second version of the application and then replace it. While the TTI is weak in CSR solutions, at least the website does explicitly look like it is still not yet fully loaded and the user does not expect it to be interactive. However, in universal solutions, the page often appears fully-loaded and ready, while it is not. It can be confusing in cases where the rehydration process takes too long. Whether or not, this is a problem depends on the use case. The combined rendering makes sense in web applications where the initial landing page is predominantly static, with a design not indicating interactivity. It can help to grasp a user’s attention until the application fully loads in the background [18]. Combining the approaches also improves SEO because the server can deliver a static HTML file to web crawlers. The web crawlers can extract the necessary metadata and ignore the content rendered by JavaScript. The popularity of universal applications is on the rise, and new approaches to the prob- lem of rehydration start to appear. One of them is partial rehydration, which instead of initiating the whole application at once, rehydrates the application by parts, starting with

10 2. Modern Frontend Development the most important. This way, the application feels interactive sooner because running the blocking JavaScript code is deferred. Another technique, combining the CSR, SSR and service workers, is called trisomorphic rendering. In this method, the initial page and navigation behave as in isomorphic appli- cation, but once the service worker is installed, it takes over the navigation and prerenders the views [18]. All of the most popular frameworks for building client-side rendered SPAs also has an offshoot framework for combining with SSR. Angular has Angular Universal, for developers fluent in React, Next.js might be a right choice, while for those accustomed to Vue, there is Nuxt.js.

2.4 Modern JavaScript Frameworks

There are numerous JavaScript frameworks suitable for the development of modern web applications. Most of them adopt a component-based architecture, splitting the application into a hierarchy of components, making the individual parts more reusable. They usually provide some implementation of a data binding technique, which binds the component’s data with HTML view and updates the view each time a data change is detected. This section will briefly introduce three currently most popular JavaScript frameworks.

2.4.1 Angular

Angular is an open-source framework backed by . The first major version of Angular (then called Angular.js) was released in 2013 and later discontinued in favour of the new Angular in 2016. Compared to React or Vue, which are in its size and features more com- parable to libraries, Angular is a full-blown framework with clearly defined architecture and out-of-the-box features, including HTTP requesting, utilities for form building, depen- dency injection, animations, internationalization, and a test runner. This section presents some of the core concepts of Angular based on the official documentation [24] and goes into detail in topics required for better understanding of later chapters. The Angular development team decided to wager on some external libraries, making them a core part of the framework. TypeScript is an indivisible part of Angular, as all of the Angular code is also written in TypeScript. Therefore, the usage of TypeScript is strongly encouraged in Angular applications. Similarly, Angular is bundled with RxJs, a popular library supporting the reactive programming paradigm in JavaScript. Reactive program- ming is an asynchronous programming paradigm concerned with data streams and the propagation of change. RxJs is based on the observer pattern [25], known from software engineering theory, by creating data streams called observables. Observables can be filtered, mapped, or otherwise altered through numerous chainable operators. One or multiple ob- servers can subscribe to the observable and receive updates each time, the observed data

11 2. Modern Frontend Development

AppModule

FeatureModuleA FeatureModuleB

Figure 2.5: The hierarchy of modules in an Angular application. changes. RxJs is directly used in parts of the Angular framework, mainly in the parts con- cerned with routing and HTTP requesting. The architecture of Angular consists of three building blocks:

• modules,

• components,

• services and dependency injection.

Modules, components, and services are TypeScript classes decorated with special Angu- lar decorators. Those decorators define the type and provide metadata instructing Angular on how to use them.

Modules

Modules define a compilation context for a set of components. Each module can declare one or more components (but a component can only be declared once), import other mod- ules, export components, and provide services. Each Angular application consists of at least one root module (conventionally named AppModule) and an unlimited amount of feature modules. Encapsulating distinct application features helps to organize code in functional blocks, reusable in different contexts. Creating feature modules also helps to optimize bundle size of an application because Angular routing system allows to lazy-load modules when a specific path is activated.

12 2. Modern Frontend Development

Components

Components define views and view-related logic that Angular uses for rendering. Anycom- ponent can be injected with services. Services are recommended place for encapsulating business logic and other logic, which is not closely related to the view. The component consists of a decorated TypeScript class. The decorator contains meta- data for Angular such as component’s template, styles, or selector. The class itself contains all code related to the component. A template is an HTML file mixed with Angular specific template language (using directives). The component can also contain CSS, either directly in the metadata or in a linked file. By default, the component encapsulates its styles, - ing they will not match to same selectors of different components. For example, the h1 selector of ComponentA will not match the h1 selector of ComponentB. Each Angular application must contain a root component. All of the other components in the application are either children or descendants of the root component. The @Component decorator is an extension of the @Directive decorator. Directives are classes containing instructions for rendering the DOM from templates. Angular offers pre- defined directives, but custom directives can be declared within modules. The directives can be split into two types:

• Structural directives - alter the template by adding, removing, and replacing DOM elements.

• Attribute directives - change the appearance and behaviour of an element, similarly to regular HTML attributes.

An example of structural directives are the predefined *ngIf and *ngFor directives. The *ngIf directive manipulates the DOM by selecting one of two templates based on a condi- tion (in this case, one template is the actual element on which the directive is placed, the other is an empty template, displayed in the case when the condition evaluates to false). The *ngFor directive takes an array and iterates over it while creating a template for each element of the array. For an example of the attribute directive, predefined ngClass directive can be used to alter the class attribute of an element. To ensure that the templates always display the latest data in the DOM, Angular pro- vides a change detection system using a technique called data binding. Data can be bound with the template in three possible ways:

13 2. Modern Frontend Development

• from source to view,

• from view to source,

• two way.

Interpolation is an example of one-way data-binding from the data source to the view. The emission of events does the data-binding in the opposite direction. Data-binding is one option of communication between components in a parent-child relationship. Components in other forms of relationship (siblings, no direct relationship) should communicate either through a common parent or a service.

1 @Component({

2 selector: 'employees-list',

3 template: `

Selected employee:{{ selected }}

4

5 *ngFor="let employee of employees"

6 [name]="employee"

7 (employeeSelected)="onSelectionChanged($event)">

8 `,

9 })

10 export class EmployeesListComponent{

11 employees: string[] = ['John Doe', 'Jane Doe'];

12 selected: string= '';

13

14 onSelectionChanged(selected: string){

15 this.selected= selected;

16 }

17 }

Listing 2.1: An example of an Angular component

Services and Dependency Injection

In software engineering, dependency injection (DI) is an implementation of the inversion of control (IoC) principle. The IoC principle inverts the program’s traditional flow by calling custom parts of the program from a generic framework defining the flow. A benefit ofDIis that it promotes reusability and modularity, by making parts of the code loosely coupled. Utilizing DI removes the responsibility of instantiating classes from a class that uses them internally. The class states them as dependencies, and the DI framework handles the instan- tiation. The responsibility is transferred to the DI framework. Combined with the usage of

14 2. Modern Frontend Development interfaces, it is a powerful tool, allowing reuse of code in different contexts by swapping various implementations of interfaces [26]. Angular has its own DI framework, directly connected to the concepts of modules and components. Angular registers any class decorated with the @Injectable decorator to the DI framework. Such classes are often referred to as services. Services need to be included in a provider, in order to be injected by the DI. Providers can be declared in the root, module, component, or view layer. The layer, in which a service is provided, determines the scope of the service. Services provided at the root level are global singletons injectable by any component or service in an application. Providing service at the module level narrows the scope to components and services declared or imported in the same module. Component providers can be declared at the level of the component or the level of the view. The component level narrows the scope of service to the component and its children (each instance of a component is injected with its own instance of a service), while the provider at the view level limits the scope strictly to the component (children components will not receive the same instance). Angular splits those layers into two hierarchies.

• ElementInjector hierarchy,

• ModuleInjector hierarchy.

The DI framework always tries to satisfy the dependency with the closest provider. If the component declares a dependency, Angular traverses the element hierarchy from the component itself, through its parents, until it finds the provider or reaches the root com- ponent. In cases where the framework does not find the provider in the element hierarchy, it switches to the module hierarchy and traverses modules from the module declaring the component to the root module, again, stopping when it finds a provider or reaches the root module. It displays an error when it does not find a provider. A provider dictates the way how the injector provides the run-time version of a depen- dency. The concept of providers, together with the injector hierarchies is a powerful tool en- abling reuse of components in different contexts (modules). Dependency can be provided as:

• Literal object - the injected class itself.

• Substitute class - a class implementing the same interface.

• Existing object - an already existing instance, created outside the DI framework.

• Factory function - a function creating the desired instance dynamically at the run- time.

15 2. Modern Frontend Development

Module A

Module scoped service Component B Component A Child Component B

Component A scoped service Child Component B

ModuleInjector hierarchy of Module A hierarchy of ModuleInjector scoped service Child Component B ElementInjector hierarchy of

ElementInjector hierarchy of Component A ElementInjector hierarchy of Component B

Figure 2.6: Difference between the ModuleInjector and ElementInjector hierarchy.

16 2. Modern Frontend Development

2.4.2 React

React is an open-source library developed by Facebook, first released in 2013. Compared to Angular, its scope is much smaller, as the core library focuses on building reactive component- based UI with declarative views. It does not include features such as routing, state manage- ment, or HTTP requesting, and developers have the freedom to choose any third-party- developed library. React also does not enforce TypeScript and is written in JavaScript. How- ever, writing React applications in TypeScript is possible. This section describes core con- cepts of React, based on the official documentation [27]. Instead of declaring HTML templates with custom language extensions (directives), as in Angular and Vue, in React, views are created programmatically using JSX. JSX is a syntax extension of JavaScript, which resembles templating language but allows for full JavaScript expressions inside declared blocks. React’s philosophy is that view logic, and HTML markup is highly cohesive, and separating them into different files (like Angular and Vue does) is only an artificial separation of technologies, while the logic remains tightly coupled.

Components

The React architecture consists of components creating a hierarchy tree. By using virtual DOM, React can efficiently re-render a component on each data change, making thecom- ponents idempotent. In React, a component can be a function or a class extending the React.Component inter- face and implementing the render method. Each component has properties (called props) and a state (called state). Properties is an object created by React, consisting of all proper- ties passed on from a parent component. Properties are strictly read-only. A state is another object with special meaning. It is a private object used and changed internally by the component. The state serves as a snapshot of the component in a time- frame, and it should never be altered directly. Instead, the setState method should be utilized, in order to trigger re-rendering of the component, thus creating a new snapshot. React only supports one-way data flow. A component encapsulates the state. A compo- nent can only affect the behaviour of its descendants by passing parameters to child com- ponents. When a component needs to communicate with its parent, a callback function can be passed as a property. In cases where multiple components need to share a state, it is recommended to lift the state to the highest common predecessor and pass it down through the hierarchy as properties. This design choice ensures that the state is a single source of truth and the state of all components is synchronized, but it requires writing more boilerplate code than in the case of two-way data-binding.

17 2. Modern Frontend Development

React also provides a way of handling element events. The syntax is similar to DOM events, but it fully utilizes JSX. Events are handled by custom callback functions, which need to be bound in the constructor of each component. Because the template is created programmatically, in JSX, features such as the condi- tional rendering of elements, or creating elements in loops, are very similar to conditional and looping statements in plain JavaScript. When creating elements in loops, Reacts needs to have a way to track the elements when they are manipulated (removed, added, changing order), in order to avoid re-rendering them all with each change. For this purpose, a unique key is passed to each element. Keys do not need to be globally unique, but they should be unique in the scope of currently rendered component (among its siblings).

1 class EmployeesListComponent extends React.Component{

2 employees=["John Doe","Jane Doe"];

3 constructor(props){

4 super(props);

5 this.handleEmployeeSelected= this.handleEmployeeSelected.bind(this);

6 this.state={ selectedEmployee: null};

7 }

8 handleEmployeeSelected(employee){

9 this.setState({ selectedEmployee: employee });

10 }

11 render() {

12 const selectedEmployeeEl=

13 this.state.selectedEmployee !== null?(

14

Selected employee:{this.state.selectedEmployee}

15 ): null;

16 const employeesEls= this.employees.map((employee) => (

17

18 employee={employee}

19 onEmployeeSelected={this.handleEmployeeSelected}

20 />

21 ));

22 return (

{selectedEmployeeEl}{employeesEls}
);

23 }

24 }

Listing 2.2: An example of a React component

18 2. Modern Frontend Development

2.4.3 Vue

Vue is an open-source framework for creating user interfaces, developed purely by the open- source community and unlike Angular and React, it is not backed by any company. It was initially released in 2014. Vue is primarily concerned with a view layer of an application, but there are optional companion libraries providing features such as state management and routing, directly in charge of the Vue development team. This section describes the core concepts of Vue based on the official documentation [28]. Vue applications are generally written in plain JavaScript, with templates in markup extended of Vue-specific directives and elements. A considerable advantage of Vue isthat it can easily scale up by importing other Vue libraries supporting complex applications, but it can also scale down. In the most basic version of the library, it can be imported as an external script directly in HTML, and it does not need an external build system, like Angular and React do.

Components

Similarly to Angular and React, the component is the core building block of Vue applica- tions. Every application starts with creating an instance of Vue. This instance is the root of all components’ hierarchy tree (another Vue instances) in the application. A component and its behaviour are altered by assigning custom functions to special attributes of a Vue instance. Vue automatically tracks changes of everything stored in its data attribute and updates the DOM accordingly. Custom methods, overriding of lifecycle hooks, template and style binding is implemented similarly. Computed properties are an interesting feature of Vue, allowing for creating functions derived from a component’s data. Vue automatically recognizes the dependency and can compute the value only once and then cache it, until one of the dependant data properties is changed. Vue provides a wide offer of predefined directives for conditional rendering, list render- ing, event handling, and data bindings. Custom directives are supported. Vue supports one-way data binding between child and parent component by passing properties from parent to children and by emitting custom events the other way. It also supports two-way data binding with the v-model directive.

19 2. Modern Frontend Development

1

9

Listing 2.3: An example of a Vue component

2.5 Summary

This chapter introduced the basic concepts of modern web development. It has done so by describing the core technologies that web pages and web applications use in the first section. In the subsequent section, types of web applications and their differences were defined, followed by the current approaches to rendering web applications. In the end, an overview of popular JavaScript frameworks was presented. However, selecting the right approach and the right framework is only a part of the job. Without clearly defined architecture and best practices followed by an organization and development teams, any project will sooner or later face problems. The following chapter will set the theoretical concepts covered in this chapter into an environment of a specific organization and its projects.

20 3 Environment

This chapter sets the context of this thesis and introduces the environment in which it was created. Section 3.1 covers the environment from an organizational perspective of CSIRT- MU and its projects, which are related to this thesis – KYPO Cyber Range Platform, Security Dashboard, and ORION. Section 3.2 introduces the author’s work on those projects and how it relates to this thesis topic. Afterwards, the architecture and the technology stack of CSIRT- MU projects related to this thesis are introduced in section 3.3. The motivation and needs for a unified framework and architectural patterns for developing web applications onthe CSIRT-MU level are described in section 3.4. Sections 3.5 and 3.6 formulate functional and non-functional requirements on the implementation of the framework.

3.1 Organizational Context

3.1.1 CSIRT-MU

CSIRT-MU [29] is a cybersecurity team of Masaryk University and the first certified cyberse- curity team in the Czech Republic. It was established in 2009, and it is involved in activities such as cybersecurity research and protection of the infrastructure of Masaryk University. Main areas of interest of CSIRT-MU are:

• Incident handling and protection of Masaryk University IT environment.

• Research and development of technical solutions for education, training, penetration testing and other forms of proactive security.

The organization employs more than 30 people (not all of them on full-time workload) and cooperates with students of Masaryk University, mainly by leading their bachelor’s and master’s theses. The organizational structure of the CSIRT-MU team consists of a team leader at the top and two heads of groups. Each group contains several specialists and teams which are usually more project aligned. The groups are:

• Incident Response Group,

• Proactive Security Group

3.1.2 Projects KYPO Cyber Range Platform

KYPO Cyber Range Platform (from now on referred to as KYPO) is a project developed and lead by CSIRT-MU since 2013. In November 2020 it was released as open-source software

21 3. Environment by Masaryk University. The current version of KYPO is a third generation of the project, de- signed with experience gained from the previous generations, using modern architectural and technological approaches such as containerization, infrastructure as a code, microser- vices, and open-source software. KYPO is a platform and a virtual environment suitable for cybersecurity education, research, and training. It allows for creating virtual networks in a cloud environment and simulating attacks in a safe, controlled, and isolated environment. The platform is highly scalable and flexible, suitable for small experiments as well asex- tensive training scenarios. The platform allows for creating virtual networks with network devices similar to those used in real networks, connecting to the internet, and running full- featured operational systems. Instructors can monitor the progress of training in real-time and also use the collected data for analysis [30, 31, 32].

Security Dashboard

Security Dashboard is an internal CSIRT-MU project aiming to create a platform containing all security-related information of an organization. It is in development since 2018 because of a need to create a unified user interface for data from various tools and sources utilized in the context of the organization’s security. Goals of the dashboard are to:

• Allow creation and configuration of overviews on key metrics and performance in- dicators of protected infrastructure.

• Visualize important security-related information for members of the security staff of an organization in various roles as well as managerial overviews for different work- places.

• Provide user interfaces for operating internal tools.

A dashboard-like unified user interface can display data from various tools and sources in meaningful overviews, visualizations, and details at the appropriate abstraction level for the intended user. Unification and consolidation of various data sources can reduce the cognitive load of its users by reducing a need to switch between numerous tools and its interfaces. Typical users of Security Dashboard are:

• Incident handler of a CSIRT team.

• Chief Information Security Officer of an organization.

• Employee of Center of Information Technology of a faculty.

• Directors, managers, heads of an organization and its parts.

22 3. Environment

ORION

ORION is a research project developed and lead by members of CSIRT-MU since 2020 [33]. It focuses on research and development of advanced security orchestration and intelligent threat management. The output of the project are tools for automation and streamlining of the operational activities of security teams. Expected results of this project are software tools providing data views on critical in- frastructure, tools for security orchestration and managing security threats’ lifecycle, and a collaborative environment for members of a security team. The expected result of the project includes a unified user interface in the form of a web application.

3.2 Author’s Work

Author of this thesis started the cooperation with CSIRT-MU in 2018. During this year, he was working on the implementation of the KYPO project. Then, the current version of the KYPO project was in stages of early development. Both backend services and frontend web application were developed at the same time. At the end of the year, a prototype with core functionality was finished. Throughout the year, the author learned new concepts ofsoft- ware engineering and development of large-sized applications in Angular. In 2019, the KYPO project’s development continued, more functionality and features were added, and the existing functionality was being polished and improved. Two new developers were added to the frontend development team. During this year, the develop- ment team found out that some parts of the application were in technical debt due to sub- optimal architecture of the application and ever-changing requirements. With the planned open-source release in mind, it became apparent that the application needs to go through extensive refactoring. The author and his team started thinking about how specific archi- tecture and design patterns would help with the development. In this thesis, the author applied the skills and knowledge gained during the work on CSIRT-MU projects, and have laid out concrete architectural and design patterns, best prac- tices, and created a shared set of libraries for better development of Angular applications at CSIRT-MU. The architecture and design patterns are, besides other topics, discussed in chapter 4. Chapter 5 describes the implementation of the framework and its individual parts. Application of the framework in existing projects is described in chapter 6.

3.3 Technological Environment

3.3.1 Architecture of KYPO

KYPO is designed with microservice architecture in mind, and it consists of several mi- croservices and a web user interface. Microservice architecture is a style of designing soft-

23 3. Environment ware as a collection of services [34]. According to Lewis and Fowler [35], characteristics of microservices are: • Highly maintainable and testable, • loosely coupled, • independently deployable, • built around business capabilities. In KYPO, the microservice architecture allowed for a simultaneous development by multiple teams with different technological stack (Python, Java, Angular) and reusability of some services in other projects (User & Group service). The communication between the services is realized through the REST API. The architecture of KYPO is shown in diagram 3.1. Main microservices of KYPO are: • OIDC Provider - OIDC (OpenID Connect Authentication) is a simple layer on top of the OAuth 2.0 protocol. OIDC provider is an authorization server allowing clients to authenticate against it by using REST API [36]. KYPO uses OIDC provider for user authentication. • User & Group Service - manages a membership of users in groups and its roles. The service communicates with the OIDC provider during the authentication process. Other microservices register to the User & Group service. This service manages user access rights within KYPO. • Sandbox Service - it manages sandboxes and its lifecycle. Through this service, sand- box definitions are uploaded to KYPO and instantiated in a cloud environment. The service also handles various configurations, networking, and specific adjustments of sandboxes. • Elasticsearch Service - communicates with the Elasticsearch data store where all training-related events are stored. • OpenStack Driver - serves as an adapter to the cloud environment. It hides imple- mentation details of the OpenStack environment and lets the client handle creating, deleting, and manipulating of virtual infrastructure and virtual machines through its API. • Training Service - Manages the whole lifecycle of capture-the-flag cyber exercises (training) in KYPO. The service allows for defining training and its levels, creating instances of training (individual exercises), managing the training, connecting to sandbox instances, and playing the game.

24 3. Environment

<> OIDC Provider

<> KYPO GUI

<> User & Group Service

<> <> <> Elasticsearch Service Training Service Sandbox Service

<> Openstack Driver

<> OpenStack Cloud Gitlab

Figure 3.1: Component diagram of the KYPO architecture.

25 3. Environment

• KYPO GUI - The GUI is implemented as a separate Angular web application. The application communicates with Training, Sandbox, Elasticsearch, User & Group ser- vices and an OIDC provider through REST API. Individual parts of the application render differently based on the roles of an authenticated user.

• OpenStack Cloud - provider of the virtual environment and machines (infrastruc- ture as a service).

• Gitlab - used for storing and versioning templates of sandbox definitions.

Sandbox Service and OpenStack driver are described in detail in Staněk’s thesis [37].

3.3.2 Architecture of Security Dashboard

Similarly to KYPO, Security Dashboard also employs the microservices architectural pat- tern. The pattern complements the plugin-like nature of the project. There is a monolithic web application on top of the individual services, communicating with the services through REST API. The architecture of Security Dashboard is shown in 3.2 Main microservices of Security Dashboard are:

• OIDC Provider - Security Dashboard uses OIDC provider for user authentication.

• IP Blacklist - manages blacklisted IP addresses and allows for requesting new addi- tions to the blacklist.

• IP Whitelist - manages whitelisted IP addresses and allows for requesting new ad- ditions to the whitelist.

• Email Blacklist - manages blacklisted email addresses and allows for requesting new additions to the blacklist.

• User Blacklist - manages blacklisted users and allows for requesting new additions to the blacklist.

• Security Dashboard GUI - the GUI is implemented as a separate Angular web appli- cation. It communicates with IP blacklist, IP whitelist, Email blacklist, User blacklist services, and an OIDC provider, through REST API.

26 3. Environment

«service» «service» «service» External Service OIDC Provider External Service

«service» «service» IP Blacklist Service IP Whitelist Service

«component» Security Dashboard GUI

«service» «service» Email Blacklist Service User Blacklist Service

«service» «service» External Service External Service

Figure 3.2: Component diagram of the Security Dashboard architecture.

3.3.3 Development Technologies

This section introduces notable technologies and approaches used in CSIRT-MU, and dur- ing the development of KYPO and Security Dashboard.

Java

Java is one of the most commonly used programming languages. It is a statically typed object-oriented language. Combined with the Spring Boot framework, it is used in the Train- ing, Elasticsearch, and User & Group services in KYPO.

Python

Python is another backend language used in CSIRT-MU. It is a high-level, dynamically- typed, interpreted language suitable for large applications but also for rapid prototyping. Together with the Django framework, it is used in Sandbox service and OpenStack driver in KYPO. In Security Dashboard, all microservices are written in Python and Django frame- work.

27 3. Environment

Angular

Angular was already introduced in section 2.4. The GUIs of KYPO and Security Dashboard are written in Angular. Angular was selected as the primary frontend framework for KYPO, Security Dashboard, and all other new CSIRT-MU projects because of its rigid architecture suitable for large applications, maturity, and its interconnection with TypeScript.

Swagger

Swagger [38] is a set of tools (Editor, Codegen, UI) for simplifying API development and documentation. The core of swagger is a specification, which is a set of rules and best prac- tices for designing APIs. The Swagger Editor can generate the specification from JSON file, YAML file, or existing code. Interactive documentation in the form of a web application can be generated from the specification (Swagger UI) as well as a code for communication with the API (Swagger Codegen). Swagger supports a design-first approach, which allows for specifying an API without writing any code, as well as a code-first approach, which generates swagger specification from already existing code.

Continuous Integration, Continuous Delivery

Continuous integration and continuous development (CI/CD) is a software development and IT operations practice for fast, reliable, and safe delivery of code changes. The contin- uous integration part is a practice where each developer frequently integrates their work many times a day. Each integration should be verified by running an automated pipeline for building and testing the software. Frequent integration reduces discrepancies between different versions of the software and mitigates integration problems. It allows for faster development of cohesive software [39]. Continuous delivery is a style of building software in a way to be able to release to a production environment at any given time and in any given version. In order to be contin- uously delivered, it also needs to be continuously integrated. Automation of all parts of the delivery process is a crucial requirement of continuous delivery [40]. In CSIRT-MU, the CI/CD is used for automated code style checks, tests, builds, and deployment of internal libraries.

Build Once, Deploy Many

Build once, deploy many is a deployment strategy allowing to deploy a once build artefact into many environments. This approach is generally considered to be safer (building only once reduces complexity and a window for errors) and more cost-effective (each build costs resources) than building for each environment separately [40]. The core prerequisite of this solution is the separation of configuration from code [41].

28 3. Environment

Gitlab

Gitlab is a DevOps lifecycle tool. At its core, it is a git repository manager but provides other features such as issue tracking, wiki, continuous integration and continuous deployment pipeline, support for Agile planning, and more. It is used for all CSIRT-MU projects. In KYPO, it is also used as a private package and containers registry.

Docker

Docker is a set of tools providing virtualization on operating system level. It is used to deliver software in containers. Each container is isolated from the others and can only com- municate through well-defined channels. Usage of Docker eliminates disparity between various development and productions environments. Docker Compose is one of the Docker tools for simplifying container orchestration and starting up multiple containers [42].

3.4 Motivation for Development of Unified Framework

As the requirements on features and user-friendly interfaces grow, the development of mod- ern web application can become a complicated and costly task. A significant number of projects, prototypes, and software in CSIRT-MU are at least partially developed by students as bachelor’s and master’s theses. Practical outputs of these projects are usually in a state of a draft, prototype, or proof of concept. Integration of these outputs into more complex systems in advanced development stages is often tedious, and complex. The same applies to small projects carried out by junior developers. In backend environment, the microservices architecture allows for the development of separate units, communicating through clearly defined APIs. In the frontend environment, it is still the most common approach to build monolithic web applications communicating with several backend services. For example, the User & Group service implements a ubiq- uitous feature of user access management. It is desirable to implement it once and reuse it in many different systems. It can be done so, quite easily, because of its microservice design. On the other hand, the GUI of the User & Group service must be developed and deployed as a separate web application, making it harder to share UI components, design guidelines, and context of the systems in which it is integrated. The main goal of this thesis is to design and implement a framework consisting of architecture overview, guidelines, tools, and sys- tem of developing Angular user interfaces in the CSIRT-MU environment. The framework should accelerate the development process and reduce development costs. The architecture should make it easier to combine various Angular applications or its parts into a unified whole. The secondary goal is to apply this architecture and framework in the KYPO GUI and Security Dashboard GUI.

29 3. Environment

3.5 Functional Requirements of the Framework

• The framework contains a set of essential reusable UI components.

• The framework proposes and implements a solution for build once, deploy many approach in Angular applications.

• The framework offers methods to authenticate with an OIDC provider.

• The framework supports multiple OIDC providers based on provided configuration.

• The framework offers methods to display and access parts of the GUI based onthe user’s roles.

• The framework offers methods to display lists of resources.

• The framework offers methods to perform CRUD operations on resources individu- ally or in batch.

• The framework offers methods to perform resource-specific operations on selected resources individually or in batch.

3.6 Non-functional Requirements of the Framework

• The framework contains an architecture suitable for large Angular applications.

• The framework identifies and provides appropriate abstractions for standard fea- tures of web applications (notifications, error handling, or pagination).

• The framework describes and utilizes techniques for code sharing in the Angular environment (libraries and packaging).

• The framework libraries utilize bundle size optimization techniques such as tree shaking and lazy loading.

• Individual parts of the framework are logically separated into reusable and testable modules and components.

30 4 Analysis and Design

This chapter analyzes architecture and patterns suitable for CSIRT-MU applications and designs feasible implementation of the development framework. It proposes micro fron- tends architecture as a possible architecture of applications in the CSIRT-MU environment. In section 4.1, the concept is introduced in general, while section 4.2 shifts the concept into the Angular environment, and designs a possible implementation. Section 4.3 analyzes the feasibility of implementing micro frontends as Angular libraries. It also introduces tools, concepts, and design choices, which are indivisible parts of developing Angular libraries. Sections 4.4, 4.5, and 4.6 introduce several architectural choices and best practices for gen- eral data flow, state management, and decomposition of Angular components. A general means of communication, navigation, and decomposition of typical UI applications are dis- cussed in section 4.7. Section 4.8 describes the prospect of automatic code generation. Possi- ble solutions for supporting the build once, deploy many approach in Angular environment are analyzed in section 4.9.

4.1 Micro Frontends

In the current state of software engineering, systems with carefully decomposed microser- vices owned by separate teams are more and more common. Often, there is a large, mono- lithic frontend maintained by a single large team on top of the decomposed backend. Code- bases of such applications are often complex and entwined. Large monolith applications are harder to maintain. Micro frontends is a new architectural style extending the idea of decoupled microservices to the frontend environment [43]. Opting for microservices and micro frontends, lets an organization build vertical devel- opment teams. Vertical teams focus on a specific part of the system with end-to-end ap- proach (from a database and a backend to a frontend). Vertical teams are formed around domains rather than technology. Creating compact cross-functional vertical teams reduces the communication overhead between frontend and backend teams, improves the team’s dynamics, and is suitable for full-stack developers. Still, some form of communication on a horizontal level is advised. Otherwise, each team could come up with their own cod- ing standards, tooling, and practices, which would make transfers of developers between teams unnecessarily difficult. However, such communication is centred around technology, standards, and best practices rather than the product itself [44]. Naturally,there are downsides and trade-offs associated with the micro frontends. While on the backend side, the discrepancies between APIs of each service are not that important, in a user-facing frontend environment, any inconsistencies in the UI are undesirable. This issue can be mitigated by developing shared UI components, usage of theming, and devel- oping UI guidelines and a design system for the whole organization.

31 4. Analysis and Design

Frontend

Backend Monolith Application

Owned by the whole project team

Figure 4.1: A traditional monolith architecture. One large team working on a monolithic system consisting of many domains and technologies [43].

Owned by the frontend team

Frontend Application

Frontend

Backend

Microservice A Microservice B Microservice C

Owned by the Owned by the Owned by the microservice A team microservice B team microservice C team

Figure 4.2: The diagram shows microservices architecture combined with a monolithic fron- tend. Backend is separated into logical units based on a domain. Each service has separate codebase and is owned by a different team. The frontend is a monolithic application, owned by a single frontend team [43].

32 4. Analysis and Design

Frontend

Backend Service A Service B Service C

Owned by the Owned by the Owned by the service A team service B team service C team

Figure 4.3: The diagram shows microservices in combination with micro frontends. Each team owns one domain and is responsible for its end-to-end development [43].

Unlike microservices, which can be developed and deployed entirely separately, with micro frontends, it is usually desirable to integrate the micro frontends in a wrapping ap- plication. It is recommended to integrate frequently, possibly by automated pipelines and tests, to avoid integration issues, UI inconsistencies, or communication errors between mi- cro frontends. Integration of micro frontends into one web application can be done at the build-time or the run-time. Integration at the build-time means that each micro frontend is released separately as a JavaScript package and listed in the main application as a dependency. The downside of this approach is a need to update dependencies, build, and deploy the whole application with each dependency update. Integration at the run-time avoids the need for frequent rebuilding and redeployment, by importing the individual micro frontends as separate scripts from a server. The indi- vidual pieces can be built and deployed separately and automatically propagated into the main application. Technically, it is often accomplished by using iframes, custom JavaScript solution, or Web Components1 [45].

1. Web Components allow to encapsulate components in custom web elements.

33 4. Analysis and Design

4.2 Micro Frontends in Angular

One of the benefits of micro frontends is that, similarly to microservices, they are techno- logically agnostic. Different parts of the application can be written in different frontend frameworks and compiled to plain JavaScript [43]. However, in environments where only one frontend framework is utilized, there may not be any real added value in a technologically agnostic solution. In the case of CSIRT- MU, it was decided to try to design and implement the micro frontends architecture within the Angular framework at first. It allows for lower complexity of the solution, easier code sharing, and the possibility of utilizing features already provided by the framework such as routing, dependency injection, modularization, or packaging. If a need for technologically agnostic micro frontends arises in the future, the transfor- mation of micro frontends, developed in Angular and wrapped in an Angular application, would require modifying the build process of each unit and replacing the wrapping appli- cation by a different solution (iframes, custom JavaScript solution, or Web Components, as stated in the previous section). The proposed solution is to split an application into logical units and implement them as self-contained Angular libraries. In this thesis, a term agenda is utilized for such logical unit. An agenda can be integrated into one or multiple applications. Such partitioning can be directly derived from an architecture of a backend, allowing to create a vertical team for each domain. However, in some cases, partitioning these agendas with a different granularity can be beneficial. An application declares each agenda as a dependency, and a package management sys- tem installs the dependency from a package registry. Each agenda can state its own depen- dencies on JavaScript libraries, shared UI component library, or other agendas. Individual agendas can be easily plugged into an existing application by utilizing the module system of Angular. The architecture also needs to define a way of communication between the main appli- cation and the individual agendas. The most common forms of communication that need to be addressed are:

• Routing,

• notifications,

• error handling,

• configuration,

• sharing a state.

34 4. Analysis and Design

Events Events

Agenda A Main Application Agenda B Context Context

Events Context

Agenda C

Figure 4.4: Context diagram of communication between an application and agendas. The application provides context (configuration, state) to the agenda and listens to events (no- tifications, errors, navigation). Each agenda can be integrated into multiple applications, independently of others.

In Angular, communication between individual agendas can be achieved by defining clear interfaces and utilizing dependency injection. Angular itself already contains an inter- face for routing, but there needs to be an interface for providing correct paths to individual pages of the application.

4.3 Angular Libraries

Angular’s support for libraries can be utilized to build agendas as an independent, reusable, and installable packages. Angular supports multiple projects in a single workspace. Struc- ture of the workspace, declared projects, build options, tests settings, and other related settings are derived from a workspace configuration file named angular.. Each Angu- lar workspace contains one such file. Based on the configuration of the workspace, projects are either applications or libraries. The build process for applications differs from the one for libraries [46].

35 4. Analysis and Design

my-library workspace src projects public-api.ts

my-library entry-point-1 src src public-api.ts public-api.ts

package.json ng-package.json

ng-package.json entry-point-2

my-example-app src src public-api.ts package.json ng-package.json

package.json ng-package.json

angular.json package.json

Figure 4.5: The left folder structure shows an Angular workspace containing one library and one application. The folder structure on the right shows detail of a library with one main entry point and two secondary entry points. The ng-packagr automatically detects entry points by the presence of ng-package.json files.

36 4. Analysis and Design

Ng-packagr is a community tool for transpiling Angular libraries into Angular Package Format 2. The output of the transpilation can be packed and the package installed by other applications or libraries. There are certain best practices to follow when building an Angular library: • The library modules should not import modules for HTTP communication, routing, and other similar global modules. Instead, a client of the library should import them. • Services and components inside the library should not depend on environmental variables, as they are unavailable in the library’s scope. However, the library’s con- figuration can be injected into the scope of the library’s module through dependency injection mechanism. • All TypeScript classes expected to be used by a client need to be exported in the public-api.ts file. The packaging tool detects such files and uses them to createan API of the library. • The library can depend on other packages, but they should not be bundled with it. Instead, it is a better approach to state them as peerDependencies and let the responsibility of installing them to a client.

Secondary Entry Points

Often, it makes sense to bundle large portions of code as a single library, even though a client is not expected to use all of the code it provides. Basic UI component libraries are an excellent example of it. Even in the case of agendas, a client is not expected to import all of its modules and components at once. Instead, lazy loading of parts of the agenda is encouraged. Secondary entry points are utilized to support tree shaking and reduce the size of the final application bundle. Secondary entry points refine granularity of the library’s API and allow to import only specific parts of the library. Ng-packagr automatically detects and creates secondary entry points from a folder structure of a library, by the presence of ng-package.json file.

4.4 Architecture of an Agenda

A high-level architecture of an application consisting of individual agendas was already introduced. The architecture of each agenda should also be defined. Angular already provides and forces some of the architectural patterns directly in the framework (components, dependency injection, and modularity). However, Angular does

2. Angular Package Format describes file layout and metadata configuration that allows Angular packages to be transpiled to formats compatible with most of the standard package consuming tools in the JavaScript ecosystem.

37 4. Analysis and Design

Application

Module A Module B

imports imports

Library

EntryEntry point A A Entry point B EntryEntry point C C

Figure 4.6: Diagram showing the relationship between modules and entry points. Only imported entry points are included in the final bundle of an application.

<> API Layer «component» ResourceAPIService

<> State Layer <> <> ResourceOverviewService ResourceDetailService

<> Component Layer

<> <> ResourceOverviewComponent ResourceDetailComponent

Figure 4.7: An example of the layered architecture of an agenda.

38 4. Analysis and Design not directly provide an architecture for state management and data flow. For example, by improper usage of dependency injection and bad decomposition, the components, which should mostly be concerned with the view logic, can quickly become bloated with business logic, communication with API, or state management. Additional architecture and best practices describing the data flow are recommended. For CSIRT-MU, a layered architecture, visualized in figure 4.7, was designed. The architec- ture consists of the API layer, state layer, and components layer. The topmost API layer is strictly stateless, and its only responsibility is making HTTP requests to a microservice and mapping responses to internal model classes. There can be one API service responsible for the communication with the whole microservice, or further granularization can be applied (for example, one service per data resource). The state management layer’s responsibility is holding the state of data for components. It serves the role of a single source of truth for the component layer, and it is responsible for all client-side transformations of the data. The state management services communicate with the API layer by calling methods of its interface. State management services utilize observer pattern and provide its state to the component layer through Observable interface. Components are the lowest layer. This layer should be split into multiple layers of com- ponents, with different responsibilities and appropriate abstraction levels. The architecture of components is described in the next section. The components should only be concerned with the view logic. Any business logic, data transformation, and data retrieval should be made in the upper layers. The layered architecture leverages the Angular’s dependency injection system. With proper abstractions and clearly defined interfaces, implementations of each layer can be replaced independently of others. This approach encourages writing code with the sepa- ration of concerns and the single responsibility principles in mind. It also makes the code more testable.

4.5 Smart-dumb Components

The purpose of a component architecture is to compose the application from small presen- tational blocks, only concerned with UI logic. The previous section already made clear that all the business logic should be located in services. In practice, the components often be- come bloated with other logic, handling events, maintaining state, and just doing too many things in general. Smart-dumb architecture limits components’ responsibilities by separat- ing them into groups of smart and dumb components [47]. Smart components communicate with services and maintain state for child components. Usually, a smart component is not concerned with presentational logic. They should always be on top of a hierarchy tree of a module (in large modules, multiple smart components in the top layer might be a good approach to maintain a reasonable size of components). On the contrary, dumb components (also called isolated) should always be a child or descen-

39 4. Analysis and Design

State service

Asynchronous data Method calls

Smart Component

Events Data Events Data

Dumb component Dumb Component

Figure 4.8: A diagram of smart-dumb components architecture. dant of a smart component. A dumb component is only concerned with presentation and presentational logic. It does not hold any state unrelated to the UI. Its only communication form is receiving data from a parent component and emitting events to a parent component. No services should be injected into a dumb component. Strict compliance with the smart-dumb architecture makes components more reusable and testable. A smart component’s sole purpose is obtaining data from external sources and delegating events received from child components by calling appropriate methods of services. Tests for such components are harder to set up because they usually contain many dependencies, but the logic itself is often pretty simple. On the other hand, the architecture makes testing core logic (presentational or business) more manageable, as it is isolated in components and services with fewer dependencies.

4.6 Application State

In general, modern web applications need to maintain large amounts of data and states. There are various approaches and patterns to maintain an application’s state. Redux3 is a state management pattern popular in React environment. There are numerous state man- agement third-party libraries based on the redux pattern for Angular. The most popular state management libraries are NGRX 4, NGXS 5, and Akita 6. Using a library for maintain- ing state is useful mainly in large applications. However, it usually produces a considerable

3. https://redux.js.org/ 4. https://github.com/ngrx 5. https://github.com/ngxs/store 6. https://github.com/datorama/akita

40 4. Analysis and Design amount of boilerplate code, and it requires developers and teams to learn and agree on the usage of a specific library. By splitting an extensive application into smaller agendas, the state becomes more lo- cal and maintainable. Angular already provides features suitable for implementing state management. The second layer of the agenda architecture, shown in figure 4.7, includes a state management logic. In Angular, services dedicated to managing the state are a viable and minimalist solution in comparison to an external library. Such services make the data readable only through an asynchronous Observable. It connects to an internal data source, usually an instance of Subject class. Components subscribe to the changes in observed data and update the UI accordingly. Actions and changes of the state are initialized in com- ponents by calling a method of the state service. The service performs the action, modifies the data, and updates the state. The Angular dependency injection system can modify the scope of state services. Inject- ing the state service to a component directly, makes the state unique and available only for a component, while injecting to a module makes the state service global in the scope of the module, as described in section 2.4 of chapter 2. In agendas, no external libraries for state management are used explicitly. Application state is encapsulated in state services, while dumb components encapsulate the state of the UI. However, the usage of state management libraries is not in conflict with the state services philosophy. State services can internally use any state management libraries if they provide the data through an Observable.

4.7 Anatomy of CSIRT-MU Applications

In previous sections, the architecture and techniques suitable for a wide range of applica- tions were introduced. However, thanks to the fact that the architecture and framework are tailored to the needs of CSIRT-MU, it can include and cover use cases specific to the environment. Based on the requirements, specification, and the state of existing frontend applications such as KYPO and Security Dashboard, following repeating patterns can be observed. • The applications use a mechanism to authenticate and authorize in order to support role-based content rendering. • The applications use a standard set of components to display paginated overviews of resources (tables, cards, or lists). • The applications use a standard set of components to display notifications, error mes- sages, and confirmation dialogs. • The applications use a layout consisting of sidebar navigation, toolbar, breadcrumbs, user menu, and content of an active route.

41 4. Analysis and Design

• All of the components displaying data loaded from external sources should be able to switch between loading, error, and loaded states.

• The resource overview or resource detail pages support an integrated way of per- forming configurable actions based on the type of resource.

Creating a reusable layout, UI components, and authentication can unify the UI, and partially the UX, in all internally developed applications. It can help to reduce code repe- titions, and it can provide a method for rapid development, where each agenda, page, or component can be developed separately and continuously integrated. While supporting the CSIRT-MU way of authentication and authorization, encouraging usage of the proposed architectural best practices, and providing shared layout and compo- nents is the responsibility of the developed framework, the way how the final application is assembled from individual agendas can be achieved purely by using Angular. By splitting the agenda into Angular modules directly mapped to the individual pages containing resource views, the final application can switch those pages and views dynami- cally, using the Angular routing system. Applications concerned with displaying overviews and detail views of resources can easily follow a clear and unified structure of pages. Such structure should be reflected in the internal addressing scheme of the application, such as the one shown below.

https :// application / resource − a /: id / detail / associated − resource | {z } | {z } | {z } |{z} | {z } | {z } protocol name of the application overview page identifier resource page overview page

Following sequence describes the process of change in navigation.

1. Change in navigation is detected.

2. Angular checks with a guard service7 whether the route can be activated.

3. Any additional data are prefetched and made available for components through re- solver services8.

4. A smart component is injected into an associated RouterOutlet9.

5. The component and its children are created, initiated, and rendered.

7. Guard service is a service implementing the CanActivate interface. It decides if the navigation can proceed, for example, by checking whether a user is eligible to access the requested route [24]. 8. Resolver service is a service implementing the Resolve interface. Usually, it is utilized to fetch a resource by an identifier provided in the URL and save it to a state of the router. Then, components load the data fromthe router, instead of concerning themselves with parsing the URLs and its structure [24]. 9. RouterOutlet is a special component, which acts as a placeholder for the content of an active route [24].

42 4. Analysis and Design

Application

https://csirt-mu-application/resource-a

 Toolbar User menu Resource A Overview

Create Upload

Active agenda Navigation

Shared UI component

Figure 4.9: Wireframe of a typical CSIRT-MU application.

6. Optionally, during the initiation, the component initiates its state services and pro- vides them with necessary data.

4.8 API Code Generation

Writing code for communication with an external API is a repetitive task, frequently re- peated in many backend and frontend applications. The task often includes the creation of DTO10 classes, methods making individual HTTP requests, and mapping the DTOs to an internal model or view model. Libraries for object mapping, serialization, and HTTP requesting simplify such task, but they do not entirely automate it. Bugs can be introduced simply by mismatching attributes of internal model and data sent from the remote API, or typos in API endpoint URLs. The code needs to be updated with each change of the API, and such changes need to be communicated upfront, usually across multiple teams. Swagger Codegen is a tool for automatic generation of API client communication code from the OpenAPI Specification format. The tool supports 30 languages in various ver- sions and specific frameworks or libraries for HTTP requesting. Swagger Codegen gener- ates DTOs, classes, and methods for HTTP requesting to specific endpoints. It also generates an automated mapping (for example, JSON responses to DTOs), and bundles the code into an importable library [38]. Theoretically, microservices using swagger and OpenAPI Specification for documenta- tion could trigger a CI/CD pipeline with each change of an API, which would generate and

10. DTO - Data Transfer Object is a pattern commonly used for data transfer between remote systems.

43 4. Analysis and Design release a new API library for all supported languages. In client projects, a simple update and running integration tests would suffice to integrate the new version in most cases. This solution could fully automate the implementation of a single layer of the internal agenda architecture. In CSIRT-MU, automated API generation was tested with all frontend-facing microser- vices of KYPO. For testing, Swagger Codegen in current stable release (version 2.4.15) was used. The results for Angular in version 10 were insufficient, as in some cases, the produced code was invalid and unable to compile. There were also issues with automated bundling of the library, as the tool generated a list of incompatible dependencies (namely, the version of Angular incompatible with the version of TypeScript). Even if those issues were fixed, in many cases, methods of generated API services were generated with tens of parameters, where parameter class would be suitable. Methods with that many parameters can quickly introduce bugs by mistaking the order of the parameters. Using the adapter pattern [25] and adding an adapting layer between the API services and rest of the application would solve the issue. However, it would introduce another layer to the data flow pipeline, and it would add unnecessary complexity and integration. Ultimately, the Swagger Codegen was used for generating DTO classes, and the API ser- vice layer was programmed manually, as the effort to refactor the generated services would be too high. This experience is purely anecdotal and subjective to the tested microservices. Generating code from different documentation or for a different output language could yield better results. However, in this case, the results were far from a state expected of a reliable and automated pipeline.

4.9 Build Once, Deploy Many

Build once, deploy many is a deployment strategy allowing to deploy a single build artefact in various environments repeatedly. Building only once reduces complexity and a window for errors. It is also more cost-effective as each build costs a certain amount of resources [40]. This approach can only be utilized when an application separates configuration from a [41]. Many backend microservices in CSIRT-MU utilize the build once, deploy many approach, and it is one of the non-functional requirements of the developed frontend framework. The default way of using environment variables in Angular is incompatible with the build once, deploy many approach. In Angular, all environment variables should be located in a special environment file. In source code, all environment variables are imported from that file. The Angular project allows developers to set up multiple build configurations. One of the options of such configurations is fileReplacements. Typically, fileReplacements are utilized for substituting a default environment file with a specific environment filefor

44 4. Analysis and Design the targeted environment [48]. During the compilation, environment variables in source code are replaced by values from the environment file. Such design choice of the Angular framework makes the build once, deploy many ap- proach impossible without opting for a custom solution. In other languages and frame- works, the approach can be implemented by loading the configuration from a file, server, or database during the initiation phase. In Angular, a similar approach can be used. However, unlike a backend application, the frontend application initiates every time it is accessed. Thus, additional steps during the initiation phase will always negatively impact the load time of the application. The fastest and most sensible solution seems to be serving the con- figuration file as an asset alongside the application, to minimize the time needed tofetch it. Figure 4.10 shows the initiation phases of an Angular application. The configuration can be loaded in each step of the process. Each step has specific implementation and con- sequences.

Bootstrap the root Rest of the index.html Entry point module application

Figure 4.10: Diagram showing the initiation phase of an Angular application.

• Rest of the application phase - Loading the configuration in the last phase (mean- ing anywhere in the application) would be implemented as a global service, which fetches the configuration file in its constructor. This solution is the most straight- forward. However, it has significant drawbacks. Loading the file is an asynchronous operation, so all dependant components and services would need to implement its own logic for waiting until the configuration is loaded and available. Also, the con- figuration would be available only in the scope of components or other services. In many applications, passing the configuration or its parts to other modules andli- braries is desirable. This solution does not support that.

• Bootstrap the root module phase - The configuration can be loaded during the boot- strapping of the root module by providing a service as an APP_INITIALIZER in the root module. The benefit of this implementation is that Angular waits until theasyn- chronous operation is finished before bootstrapping entry components and services so they can access the configuration synchronously [49]. However, the configuration scope is limited as well, and the configuration cannot be passed to modules.

• Entry point phase - The configuration file of each Angular project specifies theen- try point of an Angular application. By default, it is the main.ts file. In this file, Angular loads a platform and bootstraps the root module [50]. Angular provides

45 4. Analysis and Design

platformBrowserDynamic for running the application in a browser [49]. Fetching the configuration file before the bootstrapping process makes the configuration avail- able everywhere in the Angular application scope, including modules. Implementa- tion of this solution is a bit more complicated, but it covers most of the use cases. This solution was selected as the most suitable for the framework. The implementation is described in detail in section 5.6.

• index.html phase - The earliest possible time to load the configuration file is in the index.html file. For example, the library angular-server-side-configuration11 extends the native Angular compiler. The compiler extension replaces the environ- ment variables with references to global variables. A script inside the index.html file populates these variables with environment values. The script is generated by run- ning a command of the library’s CLI. The command extracts environment variables from a Node server and generates a populating script. This option was tested as a po- tential solution. It was working correctly, but the entry point solution was selected instead, mainly because this solution adds another third party dependency, alters the compilation process, and needs to be executed inside a Node server instance.

11. https://github.com/kyubisation/angular-server-side-configuration

46 5 Sentinel Framework

This chapter follows the architecture, decomposition, and patterns of the framework, intro- duced in the previous chapter, by presenting its implementation part. It is released under the name Sentinel framework, and it is a set of libraries designed to provide an implementa- tion of standard features of modern web application in CSIRT-MU environment. Section 5.1 describes the core library of the framework. A library providing a container with a prede- fined layout and out-of-the-box features such as notifications and breadcrumbs isdescribed in section 5.2. After that, section 5.3 introduces a library for handling user authentication and authorization. Section 5.4 describes the last library of the Sentinel framework, which provides a set of essential reusable UI components. Section 5.5 follows, describing imple- mentation details of agendas. At last, section 5.6 discusses the implementation of build once, deploy many approach in the application.

5.1 Sentinel Common

Many programming languages and frameworks utilize the concept of a shared library pack- age to provide an implementation of common, frequently used utilities, abstractions, and features. Sentinel Common is a first and the most general library of the Sentinel framework. It contains various shared classes and services to support features such as pagination, val- idation, and API communication. It also contains utility classes and testing utilities. It was created incrementally, by abstracting existing code repetitions in various agendas and com- ponents. Abstract services for the state layer, described in section 4.6, are the library’s most sig- nificant added value. Generic PaginatedResourceService class defines an interface for all state services handling paginated data. The package also provides several behavioural ex- tensions of the generic state service, supporting features such as API polling or maintaining a selection of the paginated resources.

1 export abstract class PaginatedResourceService {

2 protected hasErrorSubject$: BehaviorSubject;

3 hasError$: Observable;

4 protected isLoadingSubject$: BehaviorSubject;

5 isLoading$: Observable;

6 protected resourceSubject$: BehaviorSubject>;

7 resource$: Observable>;

8 }

Listing 5.1: Example of one of the predefined state services of the Sentinel Common library.

47 5. Sentinel Framework

Benefits of the minimal, yet robust state services, are that they provide considerable freedom to a developer in the concrete implementation of features and data modifications, suited to the specific paginated resource. On the other hand, the state services enforcea common interface shared across the whole codebase, making the code more unified and testable. The library also contains the implementation of custom bootstrapping, supporting the build once, deploy many approach, discussed in section 4.9. Implementation details are thoroughly discussed in section 5.6.

1 export class SentinelBootstrapper{

2 static bootstrap(configUrl: string,

3 moduleType: Type,

4 platform: PlatformRef= platformBrowserDynamic()) {

5 fetch(configUrl)

6 .then((response) => response.json())

7 .then((config:U) => {

8 DynamicEnvironment.setConfig(config);

9 platform.bootstrapModule(moduleType)

10 .catch((err) => console.error(err));

11 })

12 .catch((err) => console.error(err));

13 }

14 }

Listing 5.2: Example of custom bootstrapper supporting build once, deploy many approach. Described in detail in section 5.6.

5.2 Sentinel Layout

Many UI libraries provide a set of components that can create a layout of an application by the composition of the components. Among the most common layout components are toolbar, side navigation, navigation drawer, breadcrumbs, or user profile menu. These com- ponents are distributed as a set of independent components, left for the developer to assem- ble and integrate. While this approach is an example case of correct decomposition, it takes significant time and effort, to assemble and integrate those components. In an environment as the CSIRT-MU one, where multiple applications or parts of ap- plications are expected to be wrapped into a unified layout, a different approach might be suitable. The Sentinel Layout library provides a single component solution that can be im- ported into any application. Custom content can be wrapped into one of the predefined layout components.

48 5. Sentinel Framework

The main benefit of the shared layout component is that it provides a shared UIcontext, in which complex production applications, components, or parts of the application under development, can run. The environment can be quickly set up for a wide scale of projects. It is another important step towards making the micro frontends architecture work by sim- plifying the integration of its parts. Technically, wrapping the content of an application is realized through the Angular’s content projection. The template projection allows specifying a position in a template of a component by inserting tag which serves as a placeholder for the projected template [24]. A client of the library can use SentinelLayoutComponent to wrap their ap- plication by projecting its content into the layout component. The layout is built on the top of components from Angular Components library 1. It assembles and configures some of the already existing components into a unified whole, but it adds many new components and features, including breadcrumbs, user menu, theming, and notification system.

1 const routes: Routes=[

2 {

3 path: 'path1',

4 resolve:{

5 breadcrumb: ExampleBreadcrumbResolver,

6 },

7 },

8 {

9 path: 'path2',

10 data:{

11 breadcrumb: 'Path2 Page '

12 }

13 }]; Listing 5.3: Example Sentinel Layout breadcrumbs integration with Angular router. The first route is using a resolver service to dynamically resolve the breadcrumb content, while the second one is declaring it directly.

5.2.1 Breadcrumbs

Breadcrumbs is a user interface component, improving user’s awareness of a location and structure of an application. Traditionally, a breadcrumb displays indicators of user’s loca- tion in a left-to-right direction, where the leftmost is the most general location, and each sequential indicator furtherly specifies the location [51].

1. https://material.angular.io/components/categories

49 5. Sentinel Framework

Figure 5.1: Screenshot of a notification of type info in Sentinel Layout.

The Sentinel Layout library provides an implementation of breadcrumbs. A developer can specify the breadcrumb title as a value or a resolver service into the Angular router path’s configuration object. The library observes changes in routing and reconstructs the breadcrumbs with each change.

5.2.2 Notifications

Notifications are another common element of modern web applications. The library pro- vides several components for displaying notifications, services for emitting and persisting notifications, and interfaces and classes representing types of notifications. Notifications are split into four categories:

• Success - displays a green notification. Informs about a successful result of an action.

• Informative - displays a blue notification. Suitable for general announcements and notices.

• Warning - displays a yellow warning. Suitable for general announcements as well as informing about consequences of specific actions.

• Error - displays a dialog window with a description of an error. Requires user’s at- tention and interaction.

Each category of a notification supports an optional follow-up event. The event is dis- played as a button in the notification body, and follow-up actions are performed after click- ing the button. It is a simple interface for implementing features such as retrying or undoing an action. In different systems, notifications may be implemented in various ways. To cover most of the use cases, the library is designed to be reusable. The service for persisting notifica- tions implements the strategy pattern [25]. The default implementation uses IndexedDB API, supported in most of the modern web browsers. The API allows for storing significant amounts of structured data in a web browser. The stored data are indexed, allowing for fast searches [52]. In cases where notifications are persisted in the backend, writing own service and overriding the default implementation is possible.

50 5. Sentinel Framework

5.2.3 Schematics

In the Angular ecosystem, a schematic is a support tool for template-based code genera- tion. It allows writing code that can modify or create Angular projects. An advantage of schematics is that they can be started directly from the Angular CLI without installing a specific library first. The CLI searches for the external dependency by itself and automati- cally starts a schematic if it is found in a package registry [53]. Sentinel Layout contains a schematic to install and automatically set up the library in new or an existing Angular project. The schematic installs the library itself as well as its other dependencies. Then, it imports the module of the library in the application’s root module and sets up a default SASS2 template. The custom schematic is another small improvement in the speed of development and the developers’ convenience.

5.3 Sentinel Auth

User-specific content is one of the essential features of today’s web applications. Imple- mentation of a login and logout process, securing access to parts of an application, and maintaining an up to date state of the user and their session is often an intricate part of implementing a web application. A motivation behind the Sentinel Auth library is to im- plement user management features in a way, to be reusable in other Angular applications in the CSIRT-MU environment. The user access logic can usually be split into an authentication part and authorization part. Authentication is a process of verifying a user’s identity, or as often explained, deter- mining whether the user truly is, who they claim to be. Authorization, on the other hand, does not verify the identity of a user, but their rights to perform specific actions in a sys- tem. In theory, the two processes are independent, and a different authority can carry each out, yet in practice, they are often combined. In CSIRT-MU, it was decided to use an OIDC provider for the authentication and internally developed User & Group microservice for the authorization. There are several existing Angular libraries, implementing the communication with an OIDC provider. The authentication part was implemented on top of the angular-oauth2-oidc library 3. The library is actively maintained and is certified by the OpenID Foundation [54]. It is an easily configurable library, providing several convenience methods for handling the communication with an OIDC provider, logging in, logging out, and acquiring information about the logged user.

2. Syntactically Awesome Style Sheets - a CSS extension used in Angular Components and Sentinel Layout libraries. 3. https://github.com/manfredsteyer/angular-oauth2-oidc

51 5. Sentinel Framework

User Sentinel Auth OIDC Provider User & Group Service

Initiate the application Check browser's local storage

[No pre-selected provider] [Has pre-selected provider]

Select provider Display providers Redirect to login

Save provider Display login page

Insert credentials

Verify credentials

[Invalid]

[Valid]

Save token Return token

Send authorization request Request user detail

Verify token

[Invalid] Display error message [Valid]

Send user details Retrieve user access details Login failed

Redirect to protected route

Login successful

Figure 5.2: Activity diagram showing the login process in the Sentinel Auth library.

52 5. Sentinel Framework

The authorization against a User & Group microservice is more straight-forward than the authentication. The process starts with the Sentinel Auth library sending an HTTP re- quest to a particular endpoint of an authorization server. The request contains a unique token obtained from the OIDC provider. The authorization server verifies the token with the OIDC provider, obtains a user’s unique identifier, and retrieves a user profile froma database. The library then receives a response containing detailed information about the user, including roles in a particular system and membership in groups. While most of the complexity of the authentication process is hidden in the library, it still takes a significant amount of effort, to set everything up correctly, configure the library and implement additional logic, including login page, logout page, or restricting access to spe- cific routes. Also, the library does not provide support for logging in with multiple OIDC providers. Without an additional abstraction layer, setting up a library for OIDC commu- nication, implementing communication and logic for the authorization against the User & Group microservices, and mainly, integration, ordering, and timing of the authentication and authorization process would need to be re-implemented with each new application. Sentinel Auth integrates the authentication and authorization processes in a single pack- age. It wraps the OIDC library with custom logic to support multiple providers in an appli- cation. The library handles the integration of the authentication and authorization author- ity and makes this two-phase process appear as one. It contains a component, which auto- matically generates a list of buttons based on configuration, for selecting an OIDC provider to log in with. The library provides classes representing a user, groups, and roles. A client of the Sentinel Auth library can secure their application routes by taking advantage of pro- vided guard services. The library is not limited to communication with the User & Group microservice. The strategy pattern [25] inspires the design of the authorization part of the login process, and by injecting own implementation of the authorization service, a different authorization logic can be used.

5.4 Sentinel Components

The last package of the Sentinel framework is a collection of UI components. The library is based on Angular Components. Angular Components library,developed directly by Google, is a popular UI component library in the Angular ecosystem. It provides a collection of high- quality, versatile components based on Google’s Material Guidelines [55, 56]. During the implementation of the KYPO GUI and the Security Dashboard GUI, the first choice was to use Angular Components directly. However, after certain progress in the de- velopment, it became evident that the projects would benefit from an own collection of com- ponents. In some cases, Angular Components were too simple, and similar logic to make them work with certain types of data were implemented repeatedly. In other cases, a set

53 5. Sentinel Framework of components were composed into larger, smarter components. However, mostly, Sentinel Components add new UI components, which are entirely absent in Angular Components. To make things clear, Sentinel Components is not a replacement nor a reimplementation of the Angular Components library. It is an extension, adopting Angular Components and Material guidelines. It is directly dependant on Angular Components, and it is expected that the Angular Components would be used alongside it. The library uses secondary entry points to support tree shaking and optimize bundle sizes. While the whole library is installed as a dependency, only the components imported and actually used in an application are present in the final bundle. Scope of the components is not clearly defined and limited. The library contains ele- mentary UI components as well as relatively complex and smart components, designed to communicate with external APIs. The components are described individually in the follow- ing subsections. Specification and requirements of the components resulted from an analysis of existing applications (KYPO and Security Dashboard GUI) and were implemented by the author of this thesis with the following exceptions:

• Bug reporting component was designed and specified by the author of this thesis. The implementation itself was a contribution of one of the KYPO developers under the author’s consultation and review.

• Stepper component was designed and specified by the author of this thesis. The implementation itself was a contribution of one of the KYPO developers under the author’s consultation and review.

• Tour guide component is based on a prototype implementation from Fedin’s bach- elor’s thesis [57]. The prototype was significantly reworked in order to support a broader spectrum of use cases. The refactoring was done by one of the KYPO devel- opers, based on the author’s consultation and review.

5.4.1 Bug Reporting

Bug reporting component was designed to allow users to report bugs and automatically create an issue in related Gitlab repository. BugReportingService controls the flow of mul- tiple components. On initiation, the service takes a screenshot of the browser and displays a dialog window. The dialog window allows drawing into the screenshot, attaching addi- tional files, and describing the bug in a markdown editor. On confirmation, the bugreport is transformed into a Gitlab issue format, and the issue is created in an appropriate reposi- tory.

54 5. Sentinel Framework

5.4.2 Control Panel

Control panel is a simple UI component that generates a sequence of buttons based on a provided array of actions. It supports simple buttons as well as a grouping actions into a dropdown menu. Individual actions can be programmatically disabled and enabled. After clicking a button, an action is triggered and processed.

5.4.3 Confirmation Dialog

Confirmation dialog is a UI component based on the dialog component from Angular Com- ponents. It displays a dialog window with a configurable message and two actions. One action is for confirmation, the second one, optional, for rejection or dismissal.

5.4.4 Error Directive

Error directive is a structural directive, similar to *ngIf. The directive can be attached to an Angular component or HTML element. Using the Angular microsyntax 4 that takes two arguments. The hasError argument takes a boolean value, deciding whether to display an error component or the actual component, to which it is attached. The tryAgain$ argument is optional, and it takes an observable of an action that should be performed in the case when a user clicks a button to try the failed action again. An advantage of using the directive is that it hides the if-else decision from a compo- nent’s template, making it more readable and less complex. It also behaves and looks uni- formly throughout the whole GUI.

5.4.5 Infinite Scroll

Infinite scroll is an abstract class, not an independent component by itself. By extending it, a component gains abilities for handling an infinite scroll behaviour. Infinite scroll isa popular UI element for displaying large amounts of data in a user friendly and performant way. The infinite scroll displays only the visible part of data, and loads sequential partsas a user scrolls through the elements and approaches the end of the current part. The Sentinel infinite scroll uses generic pagination classes from the Sentinel Common package for handling the data, and the Angular CDK viewport for displaying components in a performant way by utilizing virtual scrolling and caching. The infinite scroll behaviour can be easily plugged into any component working with state services defined in the Sen- tinel Common package.

4. The Angular microsyntax is a special syntax allowing definition of additional template variables in a string format [58].

55 5. Sentinel Framework

5.4.6 Infinite List

Displaying a paginated data in an infinite list is one of the possible implementations ofthe infinite scroll behaviour. The component takes an extension of the generic PaginatedResource class and displays it in a list with the infinite scroll behaviour. Optionally, the component can be configured to display checkboxes or radio buttons beside the elements of thelist.

5.4.7 Code Viewer

Code viewer is yet another implementation of the infinite scroll behaviour. The component takes a list of paginated strings on the input and displays them as lines of code in an infinite scroll viewport. The component is styled to resemble the visual style of code listings.

5.4.8 Loading Component

Most of the modern web applications work with data asynchronously loaded from a server. A loading indicator is a common component of many UIs. The Sentinel loading component works on the principle of Angular’s content projection. The component wraps a template and conditionally displays a loading bar above the template, based on a boolean argument. Similarly to the error directive, its goal is to display a common, frequently repeated UI element in a unified way, with minimal effort on a developer’s side.

5.4.9 Markdown Editor

Markdown is a popular light-weight markup language for creating rich text content. The component operates in an edit mode and a preview mode. A user can switch between those modes while editing the content. In the edit mode, a user can edit the content regularly, by writing the document in a format complying with the markdown syntax. They can also select from a palette of prede- fined text editing operations such as inserting headlines, changing font styles (bold, italic), inserting images, links, tables, lists, and other operations known from text editors. The oper- ations transform the content by inserting an appropriate markdown element. When a part of the content is selected, the operations wraps the selected part in the markdown element. The preview mode parses the edited document, transforms the markdown to the HTML syntax, and displays the formatted output. For parsing, the editor uses ngx-markdown 5 and marked 6 libraries. The editor can be configured to be displayed in fixed width and height with scrollbar, or in an auto-size mode, where the dimensions of the editor change accordingly to the size of its content.

5. https://github.com/jfcere/ngx-markdown 6. https://github.com/markedjs/marked

56 5. Sentinel Framework

The component can also be provided with an URL to a file store endpoint. Then, a file upload option is displayed in the editor’s footer section. The editor can upload selected files to the provided endpoint, and insert them as markdown elements for images orlinks, depending on the type of file.

5.4.10 Resource Selector

Resource selector combines several UI elements from the Angular Components library. This component aims to allow search and selection from a list of resources in a modern and dynamic user interface. The component contains an autocomplete search bar, which emits the searched value to a parent component. The parent component is expected to handle the search query and pass a list of matched resources to the resource selector component. The resources are displayed in a dropdown menu allowing for selecting a single re- source by clicking or selecting multiple resources by selecting through checkboxes. Selected resources are displayed in the search bar in the form of a UI element called a chip. A chip is essentially a highlighted text, and it can be removed by clicking on it or only by deleting the text.

5.4.11 Stepper

Stepper is a set of components and services suitable for displaying a sequence of elements in a visual style indicating the order and connection of the elements. Each step of the stepper has a configurable title, icon, and state. The component can operate in a view-only mode or an interactive mode. The interactive mode allows marking a step of the stepper as an active, by clicking on it. It can be useful when the stepper is associated with other components and serves as a control element. For example, the active step can be displayed in detail in another component. In interactive mode, the stepper can also support a drag and drop behaviour. Each step can be dragged and dropped in place of another step, changing the steps’ order.

5.4.12 Free Form

Angular provides extensive support to handle forms, either by using template-driven forms or reactive forms. The Free Form component is suitable in use cases, where a user is ex- pected to provide multiple values in separated input elements of size unknown upfront. The component lets user interactively add and remove input elements and fill them in as well.

57 5. Sentinel Framework

Figure 5.3: Screenshot of the control panel component.

Figure 5.4: Screenshot of the markdown editor component.

Figure 5.5: Screenshot of the resource selector component.

58 5. Sentinel Framework

5.4.13 Table

Sentinel Table is the most feature extensive component of the library. Displaying overviews of data in tables is a common element of user interfaces. In applications examined during the analysis, most of the overview pages contained a table. Angular Components provide a table component, which can be extended by several other components such as sorting and pagination. Those components generally require a significant amount of logic and template code to wire together and integrate with a specific data model. Sentinel Table solves this problem by adding a layer above all of these basic UI elements. The component can generate a table with appropriate column headers, rows, table cells, and sorting and pagination features, based on a data model. An advantage of generating the component based on its data model is that it is easier to create these instances of the table data model from an already existing data models of the displayed resource than it is to create a separate component, program all of its logic, code the template, and style it. Moreover, a single component for all table UI elements ensures that the UI is visually unified, and potential changes are propagated immediately toall tables. In addition to the aforementioned advantages of the table component, it also provides numerous optional features. One of the supported features is selection. If active, the first column of the table displays checkboxes, and the component handles the logic of selection from the displayed resources. Another optional feature is a built-in filter in the form ofa search bar, and integration with the loading component and error directive in given states of the table. In modern user interfaces, tables often possess a larger role than merely displaying data. The tables are often dynamic, interactive, and display visually rich content. Sentinel Table provides several interfaces to support such features. The table supports adding a list of actions to each row of a table. Each action has a configurable icon, label, state, tooltip, and a follow-up event if the action is selected. The actions are displayed in the last column of the table. If the number of actions outreaches a limit of visible actions, the actions outside the limit are displayed in a menu. By default, data cells display their content as a text. The table supports adding links to specific data cells. In that case, the content of the data cell is displayed as a link.Acontent displayed in a data cell can also be loaded asynchronously. If that is the case, the table displays a small loading spinner component, as a placeholder, inside the data cell. There is a special structural directive for specific cases where an interactive or visually rich component should be displayed inside a data cell. The directive binds a custom tem- plate to a unique identifier of a column. The table automatically detects all bound templates and renders them, instead of the default data cell content. The component, displayed in the bound template, is the same for all data cells in the col- umn. To display the content of a specific data cell, the template needs to obtain the instance

59 5. Sentinel Framework of a resource of a particular row. A technique called template outlet context is utilized to pass the instance to a bound component through data binding. The last important feature of the Sentinel Table component is expanding rows. The table can be injected with a custom component of a row detail. If so, each row can be collapsed and expanded by clicking on arrows in the first column of a row. In an animated transition, a detail of the is revealed. The detail component is dynamically rendered and destroyed, using the ComponentFactory class.

5.4.14 Tour Guide

Interactive guides for UIs of complex systems are a frequent form of a learning material. Tour guide is a complex set of services and components, able to detect specific elements in the DOM and attach an explanatory pop-up window to them. The elements that should be part of the tour, and the educational content itself, is fully configurable through an external JSON file. The file can be attached to the web application, or loaded from an external API. SentinelTourGuideService parses a JSON file, creates an internal data representation of the tour, and waits until it is started. After the tour starts, it displays a pop-up window next to a related element, and after dismissing, it continues to the next element in order. A service saves information about the tour completion to a web browser, to prevent starting the tour each time a user accesses the web application. A considerable improvement of the new version of the tour guide, in comparison to the first one, created in Fedin’s bachelor’s thesis, is that matching of the tour elementsis designed to be performed in a top-down direction. It means that the tour elements are se- lected solely by their name, and no special directives or anchors attached to the elements are required. It allows attaching tour guide stops to elements outside the control of a devel- opment team.

5.4.15 User Assignment

User assignment composes other components of the library in a single unit, able to handle use cases of assigning users to a generic resource (forming m:n relationships). The compo- nent works with the data model of a user complying with the one from the Sentinel Auth library. It displays all users available for assignment in a resource selector component. Be- low the resource selector, the Sentinel table component displays already assigned users. The table supports a selection and removal of the relationship using table actions or the control panel component.

60 5. Sentinel Framework

Figure 5.6: Screenshot of the free form component.

Figure 5.7: Screenshot of the table component.

61 5. Sentinel Framework

5.5 Agendas

Agendas, as defined in section 4.4, are implemented as independent libraries, developed in separate repositories. However, by utilizing the Sentinel framework, shared architectural choices, and continuous integration, the final GUI of an application using Sentinel Lay- out, Sentinel Components, and agendas compatible with the Sentinel framework, looks and feels unified. Each agenda follows the architecture designed in section 4.6. Each agenda’s components comply with the smart-dumb architecture as defined in section 4.5. Each instance ofan agenda library generates a bundle of a comparatively large size. Each agenda is split into modules, to reduce the final bundle size of a client’s application and allow for lazy-loading parts of an agenda. Each module is exported from a separate secondary entry point of a library, as discussed in section 4.3. The design and granularity of the entry points depend heavily on a particular applica- tion and its requirements. However, in most cases, there will need to be at least one main entry point, exporting global classes and constants. Most probably, there will also need to be a shared internal entry point, containing services and classes used by other entry points in the scope of an agenda (but not outside of it). Such composition is often necessary to prevent circular dependencies between other entry points. In cases where an agenda contains logically connected classes, for instance, services of an API layer or data model, it is often the right choice to put them into their own entry points. The granularity of decomposition of other entry points depends on the use case and other factors. However, in the two environments where the framework was applied, decomposing based on routes became a reliable rule of a thumb. Every component, service, and other classes directly tied to a page under a specific route should be bundled intoa secondary entry point and a module. Even though the agenda architecture provides noticeable independence of individual components from the application in which they are integrated, there are features whose implementation is inherently dependant on the integration environment. In general, those features can be split into two categories based on the direction of data flow. The categories are summarized in table 5.1. The problem can be furtherly illustrated on an example of a notification. An event is created deep in the components hierarchy, for example, by clicking a button, and it flows up until it reaches a layer responsible for handling it. On the contrary, inthecase of context, a component deep in the components hierarchy depends on the value provided by its ancestors. In cases where such communication is kept within the components hierarchy, both event and context communication can be handled through data binding. However, in cases where communication across multiple modules is expected, as is the case with agendas and li- braries in general, different techniques are required.

62 5. Sentinel Framework

Table 5.1: Categories of agenda and application communication.

Direction Type Implementation Examples Bottom-up Event Services and dependency injec- Notifications, error handling, tion and navigation Top-down Context Injection to singleton service in Configuration, state forRoot method of a module

To illustrate the principle on a concrete example, let us examine the implementation of notifications and communication between an example agenda and the main application. Inside the agenda, an abstract class is defined.

1 export abstract class AgendaNotificationService{

2 abstract emit(type: string, message: string,

3 action?: string): Observable;

4 }

For emitting notifications, services inside the agenda use only this abstraction. They donot care about how the notification will be handled and displayed.

1 @Injectable()

2 export class ResourceConcreteStateService extends ResourceStateService{

3 constructor(private notificationService: AgendaNotificationService,

4 private api: ResourceApi) {}

5 delete(resource: Resource): Observable> {

6 return this.api.delete(resource.id).pipe(

7 tap((_) => this.notificationService

8 .emit('success', 'Resource was deleted'))

9 );

10 }

11 }

An application integrating one or more agendas declares its own service for handling notifications on a global application level. In this case, the service converts incoming notifi- cations into a unified representation and passes it to the notification service in the Sentinel Layout library. The layout library handles persistence and rendering of a notification.

63 5. Sentinel Framework

1 @Injectable()

2 export class AppNotificationService{

3 constructor(private notificationService: SentinelNotificationService) {}

4 emit(type: string, message: string, action?: string){

5 const notification: SentinelNotification={

6 type: this.convertToSentinelNotificationType(type),

7 duration: 5000,

8 title: message,

9 action: action

10 };

11 return this.notificationService.emit(notification)

12 .pipe(map((result) =>

13 result === SentinelNotificationResult.CONFIRMED));

14 }

15 }

Since the notification service interfaces on the application level and the notification ser- vice on the agenda level are matching, the abstract notification service can be substituted by the concrete implementation by Angular’s dependency injection.

1 @NgModule({

2 providers:[

3 { provide: AgendaErrorHandler, useClass: ErrorHandlerService},

4 { provide: AgendaNotificationService, useClass: NotificationService},

5 ],

6 })

7 export class AgendaSharedModule{}

5.6 Dynamic Environments

This section describes the implementation of the build once, deploy many approach in An- gular as designed in section 4.9. The configuration file is placed in the assets folder, ensuring that the Angular compiler will bundle it with an application. The file is written in JSON format and contains configura- tion objects for individual agendas and libraries. In the main entry point of the application, SentinelBootstrapper loads the configuration file by an HTTP request before bootstrap- ping the application. An application must provide an interface of the configuration to make the configuration type-safe. After the HTTP response is received, it is deserialized into a configuration object.

64 5. Sentinel Framework

NgModule Window SentinelConfig + console + document + location ...

DynamicEnvironment ExampleAppConfig - window: ExtendedWindow ExtendedWindow +authConfig: AuthConfig + setConfig(config: SentinelConfig) + sentinelConfig: SentinelConfig +roleMapping:RoleMapping +agendaAConfig: AgendaAConfig +agendaBConfig: AgendaBConfig + getConfig(): SentinelConfig

Figure 5.8: Class diagram showing build once, deploy many approach implementation in Angular. NgModule stands for any Angular Module.

The configuration object needs to be globally available to the whole application. In JavaScript applications, global variables can be stored in the window object. The window is a JavaScript object containing references to the DOM, events, location, and other browser interfaces. By defining ExtendedWindow interface, the window interface can be extended by the sentinelConfig attribute. Access to the configuration is mediated by the DynamicEnvironment class, which defines setter and getter methods to the configuration object. Inside an application, this object is utilized to manipulate the configuration. It is set immediately after parsing the JSON file, during the bootstrapping process. Lazy-loaded modules can use the DynamicEnvironment object directly since the window object (and the configuration attribute) is already instantiated by the time they are loaded. However, when Angular preprocesses eagerly-loaded modules and try to access the con- figuration, the window is not yet instantiated (this is true only with active ahead-of-time compilation, which is default and recommended option for production applications). A factory provider from the Sentinel Common library can be utilized to make the con- figuration available in eagerly-loaded modules. The factory provider states dependency on the configuration, ensuring its execution is delayed until the configuration is loaded. Such implementation of an application’s configuration preserves all of the advantages of the Angular’s default solution and makes it possible to change the configuration dynam- ically after the application is built, which is the main prerequisite of build once, deploy many approach.

65

6 Application of the Sentinel Framework

After the framework was implemented, released, architectural patterns tested and verified on example projects, the next challenge was to apply it in existing projects. At the time of writing this thesis, the framework was successfully applied and implemented in two existing codebases. Section 6.1 discusses how was the framework applied and integrated into the KYPO GUI. Next, section 6.2 describes the application of the framework in the Security Dashboard GUI.

6.1 The KYPO GUI

Before the application of the Sentinel framework and the related refactoring, the KYPO GUI was a large monolithic application. Its codebase was already quite extensive with entwined internals and sometimes less than optimal decomposition. It was the first real application of the Sentinel framework. Refactoring allowing application of the Sentinel framework was split into the following phases. 1. Shared components, services, and classes were replaced by its equivalents from the Sentinel framework. 2. Shared KYPO-related components, services, and classes were decomposed and inte- grated as parts of standalone packages. 3. The application was split to three standalone agendas. 4. Each agenda was refactored and its data model and API classes were released as a standalone package (it was required because of a dependency of training agenda on sandbox agenda’s model and API). 5. The application was integrated with Sentinel Auth and Sentinel Layout libraries. 6. The application was refactored to use Sentinel bootstrapping and custom environ- ment system supporting build once, deploy many approach. The application lists all dependencies (for the application itself as well as for the agen- das), it defines the root of the module and component hierarchy. The main module defines a routing of the application. Each route imports a module to load lazily when a route is ac- tivated. Those modules import appropriate feature modules from the agendas and provide global services. The global services handle bottom-up communication with the agendas. Routing is con- figured directly in the application. Notifications and error handling is integrated withSen- tinel Layout. Top-down communication is handled by passing the configuration from the environment configuration to the modules of an agenda.

67 6. Application of the Sentinel Framework

Events Events User & Group Training Agenda KYPO Application Context Agenda Context

Events Context

Sandbox Agenda

Figure 6.1: Context diagram of showing the main application of KYPO and individual agen- das, following the pattern shown in figure 4.4

The main component initiates the Sentinel Auth library and wraps the whole applica- tion in a layout from the Sentinel Layout library. The router projects the components from agendas inside the layout, based on an active route. The application extends and uses the implementation of dynamic environments from Sentinel Common. The only logic, the application itself handles is definitions of access rights to the individual routes based on a user’s roles. Routes are protected by guard ser- vices. The application contains a component for displaying the home page and login page. Ever since fully-applying the framework, thanks to the extensive usage of shared com- ponents and the proposed architectural patterns, a significant part of the implementation of new features and pages was reduced to routine development tasks of integrating and configuring the individual components and applying patterns in specific components and services.

68 6. Application of the Sentinel Framework Figure 6.2: Screenshot from the KYPO GUI application after integration with the Sentinel framework.

69 6. Application of the Sentinel Framework 6.2 The Security Dashboard GUI

The second practical application of the Sentinel framework is the Security Dashboard GUI. Parts of the dashboard were developed over several years, partially by integrating compo- nents and visualizations developed as bachelor’s or master’s theses. The application lacked clear architecture, unified data flow, and state management. Components of the application contained code repetitions or the components themselves were duplicated in the applica- tion. Refactoring and application of the framework was conducted in the following phases:

1. The application was integrated with Sentinel Auth and Sentinel Layout libraries.

2. Shared generic components, services, and classes were replaced by its equivalents from the Sentinel framework.

3. Shared components, services, and classes directly related to the Security Dashboard context were decomposed and integrated as parts of standalone packages.

4. The application was split to four standalone agendas and released as packages.

After previous experience with applying the framework in the KYPO environment and since the Security Dashboard GUI is less extensive than KYPO, refactoring and application of the Sentinel framework were executed speedily, yet application in a slightly different codebase rendered a new set of improvements for the framework. Some phases were conducted in a different order to accommodate specifics of the code- base. In comparison to KYPO, the agendas were much more lightweight, only containing one page and several additional components. They also shared a large portion of code due to their similarity. Shared code was extracted and released in its own Security Dashboard Common pack- age, which provides base classes and components for all agendas. On the contrary to the KYPO application, the agendas did not need to be split into more granular packages (as API and data model packages in the case of KYPO), because they are not dependant on each other. As of now, the Security Dashboard backend microservices are not integrated with the User & Group microservice. Although its integration is planned in the future, for now, the frontend application needed to take that into account. The application uses its own autho- rization strategy against groups provided by an OIDC provider and overrides the default authorization strategy of the Sentinel Auth library.

70 6. Application of the Sentinel Framework

User Blacklist Agenda

Events Context

Events Events Security Dashboard IP Blacklist Agenda IP Whitelist Agenda Context Application Context

Events Context

User Blacklist Agenda

Figure 6.3: Context diagram of showing the main application of Security Dashboard and individual agendas, following the pattern shown in figure 4.4

71 6. Application of the Sentinel Framework Figure 6.4: Screenshot from the Security Dashboard GUI application after integration with the Sentinel framework.

72 7 Testing

In the backend environment, extensive testing is considered to be an integral part of the development process. In the last couple of years, the world of software development has witnessed a rise of several development processes and frameworks, for example, extreme programming, or test-driven-development, emphasizing the importance of testing. However, in the frontend environment, manual testing and visual verification are still widespread forms of correctness assessment of an application’s feature. Manual testing is a legitimate and valuable method of software testing. However, it should not be the only method in a software project because it is time-consuming, error-prone, and incomplete. The popularity of manual testing in the frontend environment can be attributed to a lack of quality testing tools in the past, relatively high difficulty of setting up a testing environment, or to the fact, that in frontend environment, it is often easier to test a feature manually than it is in the case of the backend environment. However, the situation is improving, and testing is becoming easier and more integral part of the frontend development. Modern JavaScript frameworks help to separate business logic from the visual logic and enforce an architecture suitable for writing unit tests. For example, Angular is bundled with testing tools by default. This chapter briefly covers general principles of testing in section 7.1. Then, section 7.2 introduces testing in the Angular environment, including some of the popular testing tools.

7.1 General Principles

There is an extensive terminology accompanying the testing environment, mostly originat- ing in the backend environment. There is testing on different levels, different methods of tests, and different types of tests. As it is an extensive topic on its own, impossible tocover thoroughly in one chapter, this section picks only the essential concepts and parts of the testing theory based on [59], directly related to the frontend and Angular testing. Testing methods can be split into multiple categories based on their characteristics:

• Manual testing - testing is conducted by a human, without any particular script, assistance, or tooling. It is flexible and easy to conduct, but also error-prone and time-consuming.

• Automated testing - testing by scripts and tooling. It is fast and cost-effective to run, but expensive to create and maintain.

• White box testing - internal structure and implementation of the tested subject is not known to the tester.

73 7. Testing

• Black box testing - internal structure and implementation of the tested subject is known to the tester. • Gray box testing - internal structure and implementation of the tested subject is partially known to the tester.

Table 7.1: An overview of testing levels.

Level Description Method Frequency Unit Testing Tests small units of a Automated, white box High system Integration Testing Tests interaction be- Automated, black box, High/medium tween individual units grey box, white box or between a unit and an external system System Testing Tests a system as a Manual or automated, Medium whole against specified black box requirements Acceptance Testing Delivery acceptance, Manual, black box Low tests compliance with business requirements

7.2 Testing in Angular

Angular is bundled with a testing environment and selected libraries for unit testing, in- tegration, and end-to-end testing. The architecture of Angular helps with decomposition and separation of concerns if carefully adhered to. Additional architectural patterns, such as state management services and smart-dumb components, help to separate presentational logic from the business logic. Mixing presentational and business logic makes testing suites harder to set up and to separate a tested subject from its environment.

7.2.1 Karma

Karma is a testing environment bundled with Angular. It creates a web server, which ex- ecutes source code against the test code. Then, the server is accessed by a set of captured browsers and tests’ results are reported to the developer. A list of captured browser can be configured in order to test the functionality in all supported environments. A browser can also become captured by accessing the web server’s URL [60].

74 7. Testing

7.2.2 Unit and Integration Testing

Angular provides several abstractions, utility classes, and functions to make testing easier and approachable. Tests for services are usually the easiest to set up. While services are usually dependant only on other services, which might need to be mocked, and contain pure TypeScript code, components, on the other hand, are integrated with services, parent components, or child components, and are directly tied to a template and styles, which makes them more complicated to set up. It is valuable to adhere to best practices and keep the business logic in services because it makes the most critical parts of an application the easiest to test. Angular provides the TestBed API, which allows for creating services and components outside the regular modules. The API can mock dependencies by substituting real services with fake ones. It also allows for overriding child components of a tested component, mak- ing it easier to simulate communication between them. The TestBed API uncovers other APIs for testing such as ComponentFixture for debugging and testing components [24]. In Angular 9, a new concept of component harnesses was introduced. Harnesses pro- vide a testing API to custom components, allowing an author of a component to provide harness implementation. Clients of these components can use the harness to control the component in tests of their own components without relying on implementation details of other components. In Angular, both unit tests and integration tests are written and run with the help of the same frameworks, tools, and runners. The main difference is in the logic of the tests itself. Unit tests should completely isolate a tested unit and mock all other dependencies. On the other hand, integration tests can test if two or more components communicate correctly. Both types of test are valuable for different scenarios. Angular is bundled with Jasmine, which can be used for both unit and integration test- ing. Jest is a popular alternative. During the implementation of practical outputs of this thesis, hundreds of unit and integration tests were written in Jasmine, mostly for the Sen- tinel framework, but also for the KYPO GUI.

Jasmine

Jasmine is a popular testing framework suitable for browser environments as well as Node.js servers. It provides a simple and readable syntax for defining test suites, individual tests, and assertions. Each test suite contains several hooks for running custom code before the first test, before each test, after each test, and after the last test. Jasmine also providesan extensive set of tools for creating spy objects, mock objects, or simulating environmental values such as the date and time [61].

75 7. Testing

1 describe('ResourceOverviewService', () => {

2 let service: ResourceOverviewService;

3 beforeEach(async(() => {

4 apiSpy= jasmine.createSpyObj( 'ResourceApi',['getAll']);

5 TestBed.configureTestingModule({

6 providers:[

7 ResourceOverviewService,

8 { provide: ResourceApi, useValue: apiSpy},

9 ],

10 });

11 service= TestBed.inject(ResourceOverviewService);

12 }));

13 it('should call api on get all', () => {

14 apiSpy.getAll.and.returnValue([]);

15 service.getAll();

16 expect(apiSpy.getAll).toHaveBeenCalledTimes(1);

17 });

18 });

Listing 7.1: Example of a test suite written in Jasmine. Tests and example of a state service.

Jest

Jest is another popular testing framework. It is not bundled with the Angular, but it can easily be installed and configured. Jest provides a simple API for creating tests suites, as- sertions, and mocking. An immense benefit of Jest is that it is capable of running testsin parallel [62].

1 describe('ResourceOverviewService', () => {

2 let service: ResourceOverviewService;

3 beforeEach(async(() => {

4 apiSpy= jest.fn();

5 jest.mock('./resource-api', () => {

6 return jest.fn().mockImplementation(() => {

7 return{getAll: []};

8 });

9 });

10 TestBed.configureTestingModule({

11 providers:[

12 ResourceOverviewService,

76 7. Testing

13 { provide: ResourceApi, useValue: apiSpy},

14 ],

15 });

16 service= TestBed.inject(ResourceOverviewService);

17 }));

18 test('should call api on get all', () => {

19 apiSpy.getAll.and.returnValue([]);

20 service.getAll();

21 expect(apiSpy.getAll).toHaveBeenCalledTimes(1);

22 });

23 });

Listing 7.2: Example of a test suite written in Jest. Tests an example of a state service.

7.2.3 End-to-end Testing

End-to-end (E2E) testing is a black-box testing methodology, aiming to test a system flow from its start to the end. It aims to simulate the behaviour of a real user. Often, it is con- ducted manually by a real person. Automated E2E testing of UI is expensive to maintain because the tests are heavily dependent on the UI’s details and the flow of actions in it. It is often recommended to cover only the core, well-documented, and stable features of an application such as the login and logout process. To at least partially hide the dependency on UI implementation details, the page object pattern is frequently utilized. The pattern recommends creating an interface for the indi- vidual pages, allowing to decouple test logic from the implementation details of UI [12]. In Angular, the tests are usually written in the same testing framework as the unit and integration tests, accompanied by a dedicated E2E testing framework. The E2E framework runs an application in an environment of a simulated browser, providing an API to control the browser and the application programmatically. Protractor is an E2E testing framework bundled with Angular. Cypress is a popular alternative. During the implementation of practical outputs of this thesis, the projects were set up to work with Cypress, and around 20 tests were written for the Sentinel Components and Sentinel Layout libraries.

Protractor

Protractor is an E2E testing framework built on top of Selenium web driver. Selenium is a tool running alongside a real web browser, allowing to simulate human interaction. Protrac- tor runs outside a web browser and the tested application, executing the commands over a network [63].

77 7. Testing

1 describe('My Protractor E2E Test Suite', () => {

2 it('should select all checkboxes', () => {

3 browser.get('https://my-application.cz');

4 element(by.id('select-all')).click();

5

6 element.all(by.css('my-checkbox')).forEach(checkbox =>

7 expect(checkbox.isSelected()).toBe(true))

8 });

9 });

Listing 7.3: Example of a test suite written in Protractor. Tests a behaviour of a select all checkbox.

Cypress

Cypress is an alternative E2E testing framework. Its main appeal lies in the fact that it uses its own web driver and it is not dependant on Selenium. Unlike the Protractor and other Selenium-based frameworks, the Cypress web driver is running in the same event loop as the tested application. It allows tests to run faster and provide better control of environment. Capturing screenshots, recording videos, and running the tests in a cloud environment are other benefits of Cypress [64].

1 describe('My Cypress E2E Test Suite', () => {

2 it('should check all checkboxes', () => {

3 cy.visit('/')

4 cy.get('select-all').click();

5 cy.get('my-checkbox').should('be.checked');

6 });

7 });

Listing 7.4: Example of a test suite written in Cypress. Tests a behaviour of a select all check- box.

78 8 Conclusion

The goal of this thesis was to analyze the current state of frontend development in general and in the context of CSIRT-MU organization, design and implement a framework support- ing rapid-development, modern software engineering architectural and design patterns based on microservices, containing a shared library of base UI components. This thesis briefly summarized the current state of modern web development andthe most relevant JavaScript frameworks for developing SPAs. It introduced several architec- tural and design patterns suitable for developing medium and large web applications in the Angular framework. It also examined the possibility of automated code generation from API documentation of backend microservices. Although the results of automated code gen- eration were insufficient, the idea should be revisited in other projects, as it might beappli- cable in cases of simple, compact APIs. Main practical output of this thesis is the Sentinel framework, which is a set of libraries providing an implementation of common patterns in web applications such as authentication and authorization, elementary state management, pagination, UI components, or support of build once, deploy many approach. The framework was released internally and is continually improved. As part of this thesis’s practical outputs, the framework and architectural patterns were applied in two existing projects. The KYPO web application was refactored and integrated with the frame- work before its open-source release in November 2020. The second practical usage of the framework is the Security Dashboard web application. Core parts of the framework were covered with unit tests written in Jasmine. The Sen- tinel Layout and Sentinel Components libraries were covered with unit tests and end-to-end tests written in Cypress. Gitlab CI/CD pipelines were set up for all of the framework’s parts to run code style checks, tests, and builds automatically. New releases of the libraries are automated as well.

8.1 Future Work

In modern software development, the release of a product seldom means the end of devel- opment. Improvements, bug fixes, and new features are continually introduced to original codebases. The same is true for the Sentinel framework. The application of the framework in two real projects already provided valuable feedback, upon which improvements were made. For the growth and development of the framework, adoption by other existing and up- coming projects is crucial. The framework has the most to offer to projects in its start because it solves many challenges frontend projects face in its preparatory and early development phases. However, the relationship is bidirectional, and the Sentinel framework should be improved and extended by new adopters’ needs.

79 8. Conclusion

New projects such as the GUI of the ORION project are already planned with the Sen- tinel framework in mind. Hopefully, it will prove itself over time, and it will improve the speed and quality of frontend development in CSIRT-MU. The obvious way of expanding the framework is by adding new components to the shared UI library. To improve the developer experience of clients of the framework, a script, generating new Angular project with Sentinel dependencies and configuration would be useful. A similar approach was already explored in the Sentinel Layout library by the usage of Schematics scripts.

80 A Sentinel Framework

Electronic attachments are located in the thesis archive of the Information System of the Masaryk University. The Sentinel framework is included as a part of the attachments in the sentinel-framework.zip file. The archive contains:

• sources directory - contains source code of all libraries of the Sentinel framework.

• packages directory - contains built packages of all libraries of the Sentinel frame- work.

• LICENSE.txt - license of the Sentinel framework.

• README.MD - Step by step guide to run parts of the framework locally in an example application.

81

B KYPO CRP

The Sentinel framework was applied and integrated into the KYPO CRP GUI. The platform was released as open-source software in November 2020. Consult the following links for more information.

• source code - available from: https://gitlab.ics.muni.cz/muni-kypo-crp/.

• documentation - available from: https://docs.crp.kypo.muni.cz/.

83

Bibliography

1. WEB HYPERTEXT APPLICATION TECHNOLOGY WORKING GROUP. HTML Stan- dard. 2020. Available also from: https://html.spec.whatwg.org/multipage/. [On- line; accessed 20-December-2020]. 2. MOZILLA DEVELOPER NETWORK CONTRIBUTORS. Populating the Page: How Browsers Work. 2020. Available also from: https : / / developer . mozilla . org / en - US/docs/Web/Performance/How_browsers_work. [Online; accessed 20-December- 2020]. 3. MOZILLA DEVELOPER NETWORK CONTRIBUTORS. CSS Basics. 2020. Available also from: https : / / developer . mozilla . org / en - US / docs / Learn / Getting _ started_with_the_web/CSS_basics. [Online; accessed 20-December-2020]. 4. GROSSKURTH, Alan; GODFREY, Michael W. A reference architecture for Web browsers. In: 21st IEEE International Conference on Software Maintenance (ICSM’05). 2005, pp. 661–664. 5. MOZILLA DEVELOPER NETWORK CONTRIBUTORS. JavasScript Introduction. 2020. Available also from: https : / / developer . mozilla . org / en - US / docs / Web / JavaScript / Guide / Introduction # What _ is _ JavaScript. [Online; accessed 20-December-2020]. 6. ECMA INTERNATIONAL. ECMAScript® 2021 Language Specification. 2020. Available also from: https://tc39.es/ecma262/#sec-intro. [Online; accessed 20-December- 2020]. 7. MICROSOFT CORPORATION. TypeScript Language Specification. 2020. Available also from: https : / / github . com / microsoft / TypeScript / blob / master / doc / spec - ARCHIVED.md. [Online; accessed 20-December-2020]. 8. GOOGLE, LLC. Search Engine Optimization (SEO) Starter Guide. 2020. Available also from: https://support.google.com/webmasters/answer/7451184?hl=en#. [On- line; accessed 20-December-2020]. 9. MIKOWSKI, Michael S.; POWELL, Josh C. Single page web applications: JavaScript end- to-end. Manning, 2014. 10. HTTP ARCHIVE. Report: State of JavaScript. 2020. Available also from: https://beta. httparchive.org/reports/state-of-javascript?start=2019_01_01&end=2020_ 01_01&view=list. [Online; accessed 20-December-2020]. 11. WAGNER, Joshua. Reduce JavaScript Payloads with Tree Shaking. 2020. Available also from: https : / / developers . google . com / web / fundamentals / performance / optimizing-/tree-shaking. [Online; accessed 20-December-2020].

85 BIBLIOGRAPHY

12. FOWLER, Martin. Patterns of enterprise application architecture. Addison-Wesley, 2015. 13. RICHARD, Sam; LEPAGE, Pete. What are Progressive Web Apps? 2020. Available also from: https://web.dev/what-are-pwas/. [Online; accessed 20-December-2020]. 14. MOZILLA DEVELOPER NETWORK CONTRIBUTORS. Features Restricted to Secure Contexts. 2020. Available also from: https://developer.mozilla.org/en-US/docs/ Web / Security / Secure _ Contexts / features _ restricted _ to _ secure _ contexts. [Online; accessed 20-December-2020]. 15. GAUNT, Matt. Service Workers: an Introduction. 2020. Available also from: https:// developers.google.com/web/fundamentals/primers/service-workers. [Online; accessed 20-December-2020]. 16. WALTON, Philip. User-centric Performance Metrics? 2019. Available also from: https: //web.dev/user-centric-performance-metrics/. [Online; accessed 20-December- 2020]. 17. NELSON, James K. Static vs. Server Rendering. 2018. Available also from: https:// frontarm.com/james-k-nelson/static-vs-server-rendering/. [Online; accessed 20-December-2020]. 18. MILLER, Jason; OSMANI, Addy. Rendering on the Web. 2019. Available also from: https://developers.google.com/web/updates/2019/02/rendering-on-the-web. [Online; accessed 20-December-2020]. 19. BREUX, Guillaume. Client-side vs. Server-side vs. Pre-rendering for Web Apps. Available also from: https://www.toptal.com/front-end/client-side-vs-server-side- pre-rendering. [Online; accessed 20-December-2020]. 20. OSMANI, Addy; BYNENS, Mathias. The cost of JavaScript in 2019. 2019. Available also from: https://v8.dev/blog/cost- of- javascript- 2019. [Online; accessed 20- December-2020]. 21. BREHM, Spike. Isomorphic JavaScript: The Future of Web Apps. 2013. Available also from: https://medium.com/airbnb-engineering/isomorphic-javascript-the-future- of-web-apps-10882b7a2ebc. [Online; accessed 20-December-2020]. 22. GOOGLE, LLC. Why Use Server-side-rendering? 2020. Available also from: https:// angular.io/guide/universal#why- use- server- side- rendering. [Online; ac- cessed 20-December-2020]. 23. OSMANI, Addy. The Cost of Client-side Rehydration. 2019. Available also from: https: //addyosmani.com/blog/rehydration/. [Online; accessed 20-December-2020]. 24. GOOGLE, LLC. Angular Documentation. 2020. Available also from: https://angular. io/docs. [Online; accessed 20-December-2020].

86 BIBLIOGRAPHY

25. GAMMA, Erich; HELM, Richard; JOHNSON, Ralph. Design Patterns Elements of Reusable Object Oriented Software. Addison Wesley, 1998. 26. FOWLER, Martin. Inversion of Control Containers and the Dependency Injection pattern. 2004. Available also from: https://martinfowler.com/articles/injection.html. [Online; accessed 20-December-2020]. 27. FACEBOOK, INC. React Documentation. 2020. Available also from: https://reactjs. org/docs/. [Online; accessed 20-December-2020]. 28. YOU, Evan; VUE CONTRIBUTORS. Vue Documentation. 2020. Available also from: https://vuejs.org/v2/guide/. [Online; accessed 20-December-2020]. 29. CSIRT-MU, INSTITUTE OF COMPUTER SCIENCE, MASARYK UNIVERSITY. CSIRT- MU. 2020. Available also from: https : / / csirt . muni . cz/. [Online; accessed 20- December-2020]. 30. MASARYK UNIVERSITY. KYPO Cyber Range Platform. 2020. Available also from: https://crp.kypo.muni.cz/. [Online; accessed 20-December-2020]. 31. VYKOPAL, Jan; VIZVÁRY, Martin; OŠLEJŠEK, Radek; ČELEDA, Pavel; TOVARŇÁK, Daniel. Lessons Learned From Complex Hands-on Defence Exercises in a Cyber Range. In: 2017 IEEE Frontiers in Education Conference [elektronická verze "online"]. Indianapolis, IN, USA: IEEE, 2017, pp. 1–8. ISBN 978-1-5090-5919-5. Available from DOI: http://dx.doi.org/10.1109/FIE.2017.8190713. 32. VYKOPAL, Jan; OŠLEJŠEK, Radek; ČELEDA, Pavel.; VIZVARY., Martin; TOVARŇÁK, Daniel. KYPO Cyber Range: Design and Use Cases. In: Proceedings of the 12th Interna- tional Conference on Software Technologies - Volume 1: ICSOFT, SciTePress, 2017, pp. 310– 321. ISBN 978-989-758-262-2. Available from DOI: 10.5220/0006428203100321. 33. MASARYK UNIVERSITY. Project Information: Advanced Security Orchestration and Intel- ligent Threat Management (ORION). 2020. Available also from: muni.cz/en/research/ projects/49407VI20202022164. [Online; accessed 20-December-2020]. 34. MASARYK UNIVERSITY. KYPO Cyber Range Platform Documentation. 2020. Available also from: https://docs.crp.kypo.muni.cz/. [Online; accessed 20-December-2020]. 35. LEWIS, James; FOWLER, Martin. Microservices: a definition of this new architectural .term 2014. Available also from: https://martinfowler.com/articles/microservices. html. [Online; accessed 20-December-2020]. 36. SAKIMURA, Nat; BRADLEY, John; JONES, Michael B.; MEDEIROS, Breno de; MOR- TIMORE, Chuck; THE OPENID FOUNDATION. OpenID Connect Core 1.0 specification. 2014. Available also from: https://openid.net/specs/openid- connect- core- 1_0.html. [Online; accessed 20-December-2020].

87 BIBLIOGRAPHY

37. STANĚK, Miloslav. Microservice for Lifecycle Management of KYPO Platform Sandboxes [online]. 2020. Available also from: https://is.muni.cz/th/iglgs/. Master’s thesis. Masaryk University, Faculty of Informatics, Brno. Supervised by Daniel TOVARŇÁK. [Online; accessed 20-December-2020]. 38. SMARTBEAR SOFTWARE INC. Swagger Documentation. 2020. Available also from: https://swagger.io/docs/. [Online; accessed 20-December-2020]. 39. DUVALL, Paul M.; MATYAS, Steve; GLOVER, Andrew. Continuous integration improv- ing software quality and reducing risk. Addison-Wesley, 2013. 40. HUMBLE, Jez; FARLEY, David. Continuous delivery: reliable software releases through build, test, and deployment automation. Addison-Wesley, 2015. 41. WIGGINS, Adam. The Twelve-Factor App. 2017. Available also from: https : / / 12factor.net. [Online; accessed 20-December-2020]. 42. DOCKER INC. Docker Documentation. 2020. Available also from: https : / / docs . docker.com/. [Online; accessed 20-December-2020]. 43. , Michael. Micro Frontends in Action. O’reilly Media, 2020. 44. HAWKS, David. Agile Team Structure. 2017. Available also from: https : / / agilevelocity . com / agile - team - structure/. [Online; accessed 20-December- 2020]. 45. JACKSON, Cam. Micro Frontends. 2019. Available also from: https://martinfowler. com/articles/micro-frontends.html. [Online; accessed 20-December-2020]. 46. GOOGLE, LLC. Angular Documentation: Creating Libraries. 2020. Available also from: https://angular.io/guide/creating-libraries. [Online; accessed 20-December- 2020]. 47. CHAUTARD, Alain. Component Architecture With Angular. 2018. Available also from: https://blog.angulartraining.com/component-architecture-with-angular- 6f7bc9165443. [Online; accessed 20-December-2020]. 48. GOOGLE, LLC. Angular Best Practices: Building and Serving Angular Apps. 2020. Avail- able also from: https://angular.io/guide/build. [Online; accessed 20-December- 2020]. 49. GOOGLE, LLC. Angular API Reference. 2020. Available also from: https://angular. io/api. [Online; accessed 20-December-2020]. 50. GOOGLE, LLC. Workspace and Project File Structure. 2020. Available also from: https: //angular.io/guide/file-structure. [Online; accessed 20-December-2020]. 51. KRUG, Steve. Don’t Make me Think, Revisited. Pearson Education, 2013.

88 BIBLIOGRAPHY

52. MOZILLA DEVELOPER NETWORK CONTRIBUTORS. Indexed DB API. 2020. Available also from: https://developer.mozilla.org/en- US/docs/Web/API/ IndexedDB_API. [Online; accessed 20-December-2020]. 53. GOOGLE, LLC. Angular Documentation: Schematics. 2020. Available also from: https: //angular.io/guide/schematics. [Online; accessed 20-December-2020]. 54. THE OPENID FOUNDATION. OpenID Certification. 2020. Available also from: https: //openid.net/certification/. [Online; accessed 20-December-2020]. 55. GOOGLE, LLC. Angular Components Documentation. 2020. Available also from: https: //material.angular.io/components. [Online; accessed 20-December-2020]. 56. GOOGLE, LLC. Material Guidelines. 2020. Available also from: https://material.io/. [Online; accessed 20-December-2020]. 57. FEDIN, Filip Daniel. Interactive Tour Guide for KYPO Portal Users. 2020. Available also from: https://is.muni.cz/th/be39s. [Online; accessed 20-December-2020]. 58. GOOGLE, LLC. Angular Documentation: Structural Directives. 2020. Available also from: https : / / angular . io / guide / structural - directives. [Online; accessed 20-December-2020]. 59. MYERS, Glenford J.; BADGETT, Tom; THOMAS, Todd M.; SANDLER, Corey. The Art of Software Testing. Wiley, 2004. 60. FRIEDEL ZIEGELMAYER. Karma: How It Works. 2020. Available also from: https: //karma-runner.github.io/5.2/intro/how-it-works.html. [Online; accessed 20-December-2020]. 61. PIVOTAL LABS. Jasmine Documentation. 2020. Available also from: https://jasmine. github.io/api/edge/global. [Online; accessed 20-December-2020]. 62. FACEBOOK INC. Jest Documentation. 2020. Available also from: https://jestjs.io/ docs/en/getting-started.html. [Online; accessed 20-December-2020]. 63. GOOGLE, LLC. Protractor Documentation: How It Works. 2020. Available also from: https : / / www . protractortest . org / # / infrastructure. [Online; accessed 20-December-2020]. 64. CYPRESS.IO, INC. Cypress Documentation: Overview - Key Differences. 2020. Available also from: https://docs.cypress.io/guides/overview/key-differences.html. [Online; accessed 20-December-2020].

89