diff --git a/factcast-site/documentation-docsy/content/en/Concept/tail-index.md b/factcast-site/documentation-docsy/content/en/Concept/tail-index.md index 8d25d65f1..f73bcf995 100644 --- a/factcast-site/documentation-docsy/content/en/Concept/tail-index.md +++ b/factcast-site/documentation-docsy/content/en/Concept/tail-index.md @@ -43,7 +43,7 @@ More precisely, FactCast maintains a certain number of rolling partial (tail) in When asked to query facts from the end of the fact log, the Postgres database now has the option to use the smaller tail index, which (as many queries are concerned with the tail of the fact log) is likely to be cached in memory. -Tail index rotation is configurable and described in [the configuration properties]({{< ref "properties.md#performance--reliability" >}}). +Tail index rotation is configurable and described in [the configuration properties]({{< ref "Setup/properties.md#performance--reliability" >}}). {{% alert title="Note" %}} Tail indexes are implemented as [Postgres Partial Indexes](https://www.postgresql.org/docs/11/indexes-partial.html). @@ -54,7 +54,7 @@ with enabled [fastupdate](https://www.postgresql.org/docs/11/sql-createindex.htm Introducing a new index does not come for free. When new facts are INSERTed, the Postgres database needs to maintain the indexes of the fact log. Hence, the higher the number of indexes, the slower the INSERT performance. -See the [recommendations of the configuration section]({{< ref "properties.md#performance--reliability" >}}) for sensible values +See the [recommendations of the configuration section]({{< ref "Setup/properties.md#performance--reliability" >}}) for sensible values on the number of tail index generations. If you encounter performance issues, see the [Postgres documentation](https://www.postgresql.org/docs/11/gin-implementation.html#GIN-FAST-UPDATE) for further advice. diff --git a/factcast-site/documentation-docsy/content/en/Setup/gRPC Client/grpc-config-basicauth.md b/factcast-site/documentation-docsy/content/en/Setup/gRPC Client/grpc-config-basicauth.md index 1b6932207..902a0488b 100755 --- a/factcast-site/documentation-docsy/content/en/Setup/gRPC Client/grpc-config-basicauth.md +++ b/factcast-site/documentation-docsy/content/en/Setup/gRPC Client/grpc-config-basicauth.md @@ -4,16 +4,6 @@ type: docs weight: 100 --- -## Access control via Basic Auth - -In order control access to a GRPC FactStore and to distinguish the two different basic client roles (ReadOnly and -FullAccess), FactCast support usage of secrets exchange via BasicAuth. Note that this is just a very basic way of -providing authentication that - if used in production - should be accompanied by using TLS in order not to expose -secrets over the wire. - -Also, the current implementation - while functional - is just a starting point. FactCast uses standard Spring Security, -so that you have plenty of options to tailor security to your needs. - ### Using BasicAuth from a client From a client's perspective, all you need to do is to provide credentials. Once the credentials are configured, they are @@ -32,97 +22,3 @@ You can always use environment variables or a `-D` switch in order to inject the see module [examples/factcast-example-client-basicauth](https://github.com/factcast/factcast/tree/master/factcast-examples/factcast-example-client-basicauth) for an example - -### Using BasicAuth on the Server - -On the server, security is enabled by default. if security is enabled, non-authenticated users will not be allowed to -work with the grpc factstore anymore. If you want to disable security set `factcast.security.enabled` to false. - -In order to enable security, a Bean of type `FactCastAccessConfig` must be defined. This is done either by providing one -in your FactCast Server's context, or by using the dead-simple approach to put a `factcast-access.json` on the root of -your classpath to deserialize it from there. - -Example below. - -Now, that you've defined the access configuration, you also need to define the secrets for each account. Again, you can -do that programmatically by providing a FactCastSecretsProperties, or by defining a property for each account like this: - -``` -factcast.access.secrets.brain=world -factcast.access.secrets.pinky=narf -factcast.access.secrets.snowball=grim -``` - -The catch with this simple approach of course is, _that credentials are stored in plaintext_ in the server's classpath, -but remember it is just a dead-simple approach to get you started. Nobody says, that you cannot provide this information -with a layer of your docker container, pull it from the AWS Parameter Store etc... - -If FactCast misses a secret for a configured account on startup, it will stop immediately. On the other hand, if there -is a secret defined for a non-existing account, this is just logged (WARNING-Level). - -The contents of factcast-access.json might look like: - -```json -{ - "accounts": [ - { - "id": "brain", - "roles": ["anything"] - }, - { - "id": "pinky", - "roles": ["anything", "limited"] - }, - { - "id": "snowball", - "roles": ["readOnlyWithoutAudit"] - } - ], - "roles": [ - { - "id": "anything", - "read": { - "include": ["*"] - }, - "write": { - "include": ["*"] - } - }, - { - "id": "limited", - "read": { - "include": ["*"], - "exclude": ["secret"] - }, - "write": { - "exclude": ["audit*"] - } - }, - { - "id": "readOnlyWithoutAudit", - "read": { - "include": ["*"], - "exclude": ["audit*", "secret"] - }, - "write": { - "exclude": ["*"] - } - } - ] -} -``` - -Where `pinky` & `brain` are authorized to use the full FactStore's functionality (with 'pinky' not being able to write -to namespaces that start with 'audit') whereas `snowball` can only read everything but 'audit'-namespaces, but not write -anything. - -In case of conflicting information: - -- explicit wins over implicit -- exclude wins over include - -Note, there is no fancy wildcard handling other than a trailing '\*'. - -see -module [examples/factcast-example-server-basicauth](https://github.com/factcast/factcast/tree/master/factcast-examples/factcast-example-server-basicauth) -for an example diff --git a/factcast-site/documentation-docsy/content/en/Setup/metrics.md b/factcast-site/documentation-docsy/content/en/Setup/metrics.md index 2f103aef7..55c7b7643 100755 --- a/factcast-site/documentation-docsy/content/en/Setup/metrics.md +++ b/factcast-site/documentation-docsy/content/en/Setup/metrics.md @@ -25,6 +25,7 @@ At the time of writing, there are six namespaces exposed: - `factcast.server.timer` - `factcast.server.meter` - `factcast.store.timer` +- `factcast.ui.timer` - `factcast.store.meter` - `factcast.registry.timer` - `factcast.registry.meter` @@ -49,8 +50,8 @@ As this list is continuously growing, we cannot guarantee the documentation's completeness. If you want to see the current list of operations, please look at [StoreMetrics.java](https://github.com/factcast/factcast/blob/master/factcast-store/src/main/java/org/factcast/store/internal/StoreMetrics.java) , [RegistryMetrics.java](https://github.com/factcast/factcast/blob/master/factcast-store/src/main/java/org/factcast/store/registry/metrics/RegistryMetrics.java) -, -or [ServerMetrics.java](https://github.com/factcast/factcast/blob/master/factcast-server-grpc/src/main/java/org/factcast/server/grpc/metrics/ServerMetrics.java) +, [ServerMetrics.java](https://github.com/factcast/factcast/blob/master/factcast-server-grpc/src/main/java/org/factcast/server/grpc/metrics/ServerMetrics.java), +or [UIMetrics.java](https://github.com/factcast/factcast/blob/master/factcast-server-ui/src/main/java/org/factcast/server/ui/metrics/UiMetrics.java) respectively. At the **time of writing (0.4.3)**, the metrics exposed by the namespaces group `factcast.server` are: @@ -132,6 +133,20 @@ You can distinguish them by the `name` tag. Currently, these are: - `subscription-factory` - used for incoming new subscriptions - `fetching-catchup` - used for buffered transformation while using the fetching catchup strategy - `paged-catchup` - used for buffered transformation while using the paged catchup strategy -- `transformation-cache` - used for inserting/updating entries in the transformation cache (only if you use persisted cache) +- `transformation-cache` - used for inserting/updating entries in the transformation cache (only if you use persisted + cache) See https://micrometer.io/docs/ref/jvm for more information. + +### UI Metrics + +Special metrics for the FactCast-Server UI are published via `factcast.ui.timer` namespace. + +| operation | type | description | +| ---------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| plugin-execution | `timer` | Time to execute a specific plugin for one fact. | +| fact-processing | `timer` | Overall time to process one fact. This includes execution of every plugin, parsing JSON payload and building the final representation model. | + +Additionally, all methods of the `org.factcast.server.ui.adapter.FactRepositoryImpl` are measured time-wise, and can be +visualized via `class` and `method` +dimension. diff --git a/factcast-site/documentation-docsy/content/en/Setup/security.md b/factcast-site/documentation-docsy/content/en/Setup/security.md new file mode 100755 index 000000000..4fbb6f408 --- /dev/null +++ b/factcast-site/documentation-docsy/content/en/Setup/security.md @@ -0,0 +1,147 @@ +--- +title: "Security" +type: docs +weight: 158 +--- + +## Authentication & Authorization + +In order to control access the FactCast Server supports a very basic way of defining which client is allowed to do what. +The security feature is enabled by default, but can be disabled (for integration tests for example) by +setting `factcast.security.enabled` to false. + +In order to make use of the security features, a Bean of type `FactCastAccessConfig` must be defined. This is done +either by providing one +in your FactCast Server's context, or by using the dead-simple approach to put a `factcast-access.json` on the root of +your classpath or at `/config/` to deserialize it from there. + +Example below. + +Now, that you've defined the access configuration, you also need to define the secrets for each account. Again, you can +do that programmatically by providing a FactCastSecretsProperties, or by defining a property for each account like this: + +``` +factcast.access.secrets.brain=world +factcast.access.secrets.pinky=narf +factcast.access.secrets.snowball=grim +``` + +The catch with this simple approach of course is, _that credentials are stored in plaintext_ in the server's classpath, +but remember it is just a dead-simple approach to get you started. Nobody says, that you cannot provide this information +with a layer of your docker container, pull it from the AWS Parameter Store etc... + +If FactCast misses a secret for a configured account on startup, it will stop immediately. On the other hand, if there +is a secret defined for a non-existing account, this is just logged (WARNING-Level). + +The contents of factcast-access.json might look like: + +```json +{ + "accounts": [ + { + "id": "brain", + "roles": ["anything"] + }, + { + "id": "pinky", + "roles": ["anything", "limited"] + }, + { + "id": "snowball", + "roles": ["readOnlyWithoutAudit"] + } + ], + "roles": [ + { + "id": "anything", + "read": { + "include": ["*"] + }, + "write": { + "include": ["*"] + } + }, + { + "id": "limited", + "read": { + "include": ["*"], + "exclude": ["secret"] + }, + "write": { + "exclude": ["audit*"] + } + }, + { + "id": "readOnlyWithoutAudit", + "read": { + "include": ["*"], + "exclude": ["audit*", "secret"] + }, + "write": { + "exclude": ["*"] + } + } + ] +} +``` + +Where `pinky` & `brain` are authorized to use the full FactStore's functionality (with 'pinky' not being able to write +to namespaces that start with 'audit') whereas `snowball` can only read everything but 'audit'-namespaces, but not write +anything. + +In case of conflicting information: + +- explicit wins over implicit +- exclude wins over include + +Note, there is no fancy wildcard handling other than a trailing '\*'. + +see +module [examples/factcast-example-server-basicauth](https://github.com/factcast/factcast/tree/master/factcast-examples/factcast-example-server-basicauth) +for an example + +### Using BasicAuth from a client + +From a client's perspective, all you need to do is to provide credentials. Once the credentials are configured, they are +used on every request in a Basic-Auth fashion (added header to request). + +In order to define credentials, just set the appropriate property to a value of the format 'username:password', just as +you would type them into your browser when a basic-auth popup appears. + +``` +# if this property is set with a value of the format 'username:password', basicauth will be used. +grpc.client.factstore.credentials=myUserName:mySecretPassword +``` + +You can always use environment variables or a `-D` switch in order to inject the credentials. + +see +module [examples/factcast-example-client-basicauth](https://github.com/factcast/factcast/tree/master/factcast-examples/factcast-example-client-basicauth) +for an example + +### Customizing Credential Loading + +If you dont want to configure your passwords via properties, you can provide either a custom `FactCastSecretProperties` +bean or an implementation of a `UserDetailsService`. +That's a simple interface coming from Spring Security which provides a mapping method from `username` to user. In our +case we have to return a `FactCastUser`. + +If you want to externalize secret loading but want to keep the `factcast-access.json` file for managing authorization an +implementation of such a `UserDetailsService` could look like this: + +``` +@Bean +UserDetailsService userDetailsService(FactCastAccessConfiguration cc, PasswordEncoder passwordEncoder) { + return username -> { + // fetching account info from fact-access.json + Optional account = cc.findAccountById(username); + + // your way to fetch the user + password + User user = loadUserByName(username); + + return account + .map(acc -> new FactCastUser(acc, passwordEncoder.encode(user.getPassword()))) + .orElseThrow(() -> new UsernameNotFoundException(username)); + }; +} +``` diff --git a/factcast-site/documentation-docsy/content/en/UI/Setup/_index.md b/factcast-site/documentation-docsy/content/en/UI/Setup/_index.md index e009aaa6d..6fef10982 100755 --- a/factcast-site/documentation-docsy/content/en/UI/Setup/_index.md +++ b/factcast-site/documentation-docsy/content/en/UI/Setup/_index.md @@ -2,7 +2,7 @@ title: "Setup" type: docs description: Setup and configuration of the Server UI -weight: 30 +weight: 10 --- This section will walk you through the many options of how to setup and operate FactCast-Server-UI. diff --git a/factcast-site/documentation-docsy/content/en/UI/Setup/plugis.md b/factcast-site/documentation-docsy/content/en/UI/Setup/plugis.md new file mode 100644 index 000000000..291e2313b --- /dev/null +++ b/factcast-site/documentation-docsy/content/en/UI/Setup/plugis.md @@ -0,0 +1,8 @@ +--- +title: "Plugins" +type: docs +weight: 200 +description: Plugins to enrich visual representation of data +--- + +## Plugins diff --git a/factcast-site/documentation-docsy/content/en/UI/Setup/properties.md b/factcast-site/documentation-docsy/content/en/UI/Setup/properties.md new file mode 100755 index 000000000..489bf9f1f --- /dev/null +++ b/factcast-site/documentation-docsy/content/en/UI/Setup/properties.md @@ -0,0 +1,12 @@ +--- +title: "Properties" +type: docs +weight: 200 +description: Properties you can use to configure FactCast +--- + +## UI + +| Property | Semantics | Default | +| --------------------- | :--------------------------------------------------------------------------------------------- | :------ | +| vaadin.productionMode | Should be set to true, otherwise vaadin tries to generate a dev bundle which is not necessary. | false | diff --git a/factcast-site/documentation-docsy/content/en/UI/Setup/security.md b/factcast-site/documentation-docsy/content/en/UI/Setup/security.md new file mode 100755 index 000000000..86235840d --- /dev/null +++ b/factcast-site/documentation-docsy/content/en/UI/Setup/security.md @@ -0,0 +1,110 @@ +--- +title: "Security" +type: docs +weight: 100 +--- + +## Authentication & Authorization + +By default, the FactCast-Server UI supports the same authentication/authorization approach as the GRPC server +explained [here](/setup/security/). + +However, you might want to make UI accessible via a centrally managed Active Directory. Luckily, we use Spring Security +under the hood and all this is possible. + +### OIDC Example + +The following example shows an OIDC integration: + +1. You have to exclude the standard configuration, that configures Spring Security & Vaadin to support the default + authentication/authorization approach. + +``` +@SpringBootApplication(exclude = {FactCastServerUISecurityAutoConfiguration.class}) +``` + +2. Add necessary Spring Security dependencies and configurations: + +``` + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.security + spring-security-oauth2-jose + +``` + +3. Implement custom security configuration + +``` +@Configuration +@EnableWebSecurity +public class SecurityConfig extends VaadinWebSecurity { + @Override + protected void configure(HttpSecurity http) throws Exception { + http.oauth2Login(Customizer.withDefaults()); // enable oauth2 login + super.configure(http); + } +} +``` + +4. Implement a `OAuth2UserService` that maps from a `OidcUser` to a `FactCastOidcUser` which is usable by the + FactCast-Server UI. + +``` +@Component +public class FactCastUserService implements OAuth2UserService { + private final OidcUserService oidcUserService = new OidcUserService(); + + @Override + public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { + // that gives you a fully authenticated user via your OAuth/OIDC provider + OidcUser user = oidcUserService.loadUser(userRequest); + + // either create the account yourself or reuse factcast-access.json here + FactCastAccount account = new FactCastAccount(user.getEmail(), List.of()); + + return new FactCastOidcUser(account, "unused", user); + } +} +``` + +with this being the `FactCastOidcUser` + +``` +public class FactCastOidcUser extends FactCastUser implements OidcUser { + private final OidcUser user; + + public FactCastOidcUser(FactCastAccount account, String secret, OidcUser user) { + super(account, secret); + this.user = user; + } + + @Override + public Map getClaims() { + return user.getClaims(); + } + + @Override + public OidcUserInfo getUserInfo() { + return user.getUserInfo(); + } + + @Override + public OidcIdToken getIdToken() { + return user.getIdToken(); + } + + @Override + public Map getAttributes() { + return user.getAttributes(); + } + + @Override + public String getName() { + return user.getName(); + } +} +``` diff --git a/factcast-site/documentation-docsy/content/en/UI/Usage/_index.md b/factcast-site/documentation-docsy/content/en/UI/Usage/_index.md index 9a5b7d8bf..9b41aeff1 100755 --- a/factcast-site/documentation-docsy/content/en/UI/Usage/_index.md +++ b/factcast-site/documentation-docsy/content/en/UI/Usage/_index.md @@ -1,8 +1,8 @@ --- title: "Usage" type: docs -weight: 10 +weight: 20 description: Usage of the FactCast-Server-UI --- -Nice pictures, please +![FactCast-Server UI](./ui.png) diff --git a/factcast-site/documentation-docsy/content/en/UI/Usage/ui.png b/factcast-site/documentation-docsy/content/en/UI/Usage/ui.png new file mode 100644 index 000000000..a66bae393 Binary files /dev/null and b/factcast-site/documentation-docsy/content/en/UI/Usage/ui.png differ diff --git a/factcast-site/documentation-docsy/content/en/UI/_index.md b/factcast-site/documentation-docsy/content/en/UI/_index.md index c0df7922b..20d7f42f5 100755 --- a/factcast-site/documentation-docsy/content/en/UI/_index.md +++ b/factcast-site/documentation-docsy/content/en/UI/_index.md @@ -6,6 +6,6 @@ menu: type: docs --- -## Factcast-Server UI +## FactCast-Server UI -What it is, what it is not +The FactCast-Server UI is a easy to use