Skip to content

Commit 00c1725

Browse files
Merge branch 'main' into readme
# Conflicts: # README.md
2 parents e83fcc3 + 210447e commit 00c1725

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2074
-616
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,5 @@ fasten.db-wal
8181

8282
backend/resources/related_versions.json
8383
frontend/documentation.json
84+
85+
certs/

README.md

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Next, run the following commands from the Windows command line or Mac/Linux term
103103

104104
### 🧪 Develop
105105

106-
Use local development settings for testing and iteration.
106+
Use local development settings for testing and iteration.
107107

108108
> ℹ️ **Observation:** Requires a local clone of the repository.
109109

@@ -132,7 +132,65 @@ docker run --rm \
132132
-v ./cache:/opt/fasten/cache \
133133
ghcr.io/fastenhealth/fasten-onprem:main
134134
```
135+
Next, open a browser to `https://localhost:9090`
135136
137+
### <a name="using-https"></a>🔒 Using HTTPS and Trusting the Self-Signed Certificate
138+
139+
By default, Fasten On-Prem runs with HTTPS enabled to ensure your data is secure. It uses a self-signed **TLS** certificate, which offers the same level of encryption as a commercially issued certificate. The first time you connect, your browser will display a security warning because it doesn't yet trust the certificate's issuer. The steps below will guide you through the simple, one-time process of telling your browser to trust the certificate, ensuring a secure connection without future warnings. Please note that the generated certificates can be replaced at any time with your own valid TLS certificates.
140+
141+
#### How it Works: The Chain of Trust
142+
143+
To establish a secure connection, your browser needs to trust the server's TLS certificate. Here’s how the process works in Fasten On-Prem:
144+
145+
1. **Root Certificate Authority (CA):** When the application first starts, it generates its own self-contained Certificate Authority, called `"Fasten Health CA"`. Think of this as the highest level of trust. The public part of this CA is the `rootCA.pem` file.
146+
2. **Server Certificate:** The application then uses the `"Fasten Health CA"` to issue and sign a specific certificate for the web server (e.g., for `localhost`).
147+
3. **Browser Verification:** When you connect to the server, it presents the server certificate to your browser. Your browser checks who signed it and sees it was `"Fasten Health CA"`. The browser then asks, "Do I trust the 'Fasten Health CA'?"
148+
149+
Initially, the answer is no, which is why you see a security warning. By following the steps below to import the `rootCA.pem` file, you are telling your browser or operating system to trust our self-generated CA. Once the CA is trusted, any certificates it signs—including the server certificate—will also be trusted, and the connection will be secure without any warnings.
150+
151+
#### 1. Locate the Root CA Certificate
152+
153+
When you run the application using the production Docker Compose file (`docker-compose-prod.yml`), it automatically generates a `rootCA.pem` file. This file is located in the `certs` directory on your host machine.
154+
155+
- **Certificate Path:** `certs/rootCA.pem`
156+
157+
#### 2. Import the Certificate
158+
159+
You will need to import this certificate into your operating system's or browser's trust store. Here are general instructions for different platforms:
160+
161+
**macOS**
162+
163+
1. Open the **Keychain Access** application.
164+
2. Select the **System** keychain.
165+
3. Go to **File > Import Items** and select the `certs/rootCA.pem` file.
166+
4. Find the "Fasten Health CA" certificate in the list, double-click it, and under the **Trust** section, set "When using this certificate" to **Always Trust**.
167+
168+
**Windows**
169+
170+
1. Double-click the `certs/rootCA.pem` file.
171+
2. Click **Install Certificate...** and choose **Local Machine**.
172+
3. Select **Place all certificates in the following store**, click **Browse**, and choose **Trusted Root Certification Authorities**.
173+
4. Complete the wizard to finish the import process.
174+
175+
**Linux (Ubuntu/Debian)**
176+
177+
1. Copy the certificate to the trusted certificates directory:
178+
```bash
179+
sudo cp certs/rootCA.pem /usr/local/share/ca-certificates/fasten-health-ca.crt
180+
```
181+
2. Update the system's certificate store:
182+
```bash
183+
sudo update-ca-certificates
184+
```
185+
186+
**Firefox**
187+
188+
Firefox has its own trust store. To import the certificate:
189+
190+
1. Go to **Settings > Privacy & Security**.
191+
2. Scroll down to **Certificates** and click **View Certificates...**.
192+
3. In the **Authorities** tab, click **Import...** and select the `certs/rootCA.pem` file.
193+
4. Check the box for **Trust this CA to identify websites** and click **OK**.
136194
137195
### Companion Mobile App
138196
@@ -152,7 +210,7 @@ In partnership with [Life Value](https://lifevalue.com), we develop an **open-so
152210
153211
### Start Fasten (discovarable on your local network)
154212
155-
For your **Fasten** instance to work together with **HealthWallet.me** (the companion mobile app), you need to run the `set_env.sh` script before starting Docker Compose.
213+
For your **Fasten** instance to work together with **HealthWallet.me** (the companion mobile app), you need to run the `set_env.sh` script before starting Docker Compose.
156214
157215
This script configures the necessary `HOSTNAME` and `IP` values in a `.env` file, which allows Fasten to generate a QR code that HealthWallet can scan to establish the initial connection and begin syncing your health data.
158216
@@ -174,9 +232,9 @@ Launch the application. Please choose a location where `docker-compose.yml` and
174232
```
175233

176234
- **Commands Breakdown**
177-
- Downloads necessary files (**docker-compose.yml** and **set_env.sh**)
178-
- The environment script automatically assigns your local IP so **Fasten** can be available on **your local network**
179-
- Starts the Fasten application (**docker-compose up -d**)
235+
- Downloads necessary files (**docker-compose.yml** and **set_env.sh**)
236+
- The environment script automatically assigns your local IP so **Fasten** can be available on **your local network**
237+
- Starts the Fasten application (**docker-compose up -d**)
180238

181239

182240
<details>
@@ -203,13 +261,7 @@ If you prefer not to run the `set_env.sh` script, you can configure the `.env` f
203261

204262
</details>
205263

206-
### Logging In
207-
208-
Before you can use the Fasten BETA, you'll need to [Create an Account](http://localhost:9090/web/auth/signup).
209264

210-
It can be as simple as
211-
- **Username:** `testuser`
212-
- **Password:** `testuser`
213265

214266

215267
## Usage

backend/cmd/fasten/fasten.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ func main() {
7676
Name: "start",
7777
Usage: "Start the fasten server",
7878
Action: func(c *cli.Context) error {
79-
//fmt.Fprintln(c.App.Writer, c.Command.Usage)
8079
if c.IsSet("config") {
8180
err = appconfig.ReadConfig(c.String("config")) // Find and read the config file
8281
if err != nil { // Handle errors reading the config file

backend/pkg/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ func (c *configuration) Init() error {
2525
c.SetDefault("web.listen.host", "0.0.0.0")
2626
c.SetDefault("web.listen.basepath", "")
2727
c.SetDefault("web.listen.https.enabled", false)
28+
c.SetDefault("web.listen.https.certDir", "certs")
29+
c.SetDefault("web.listen.https.sharedDir", "certs/shared")
2830

2931
// allow unsafe endpoints should never be enabled in Production.
3032
// It enables direct API access to healthcare providers without authentication.
@@ -33,6 +35,7 @@ func (c *configuration) Init() error {
3335
c.SetDefault("web.src.frontend.path", "/opt/fasten/web")
3436
c.SetDefault("database.type", "sqlite")
3537
c.SetDefault("database.location", "/opt/fasten/db/fasten.db")
38+
c.SetDefault("database.encryption.enabled", false)
3639
//c.SetDefault("database.encryption.key", "") //encryption key must be set by the user.
3740
c.SetDefault("cache.location", "/opt/fasten/cache/")
3841

backend/pkg/database/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
func NewRepository(appConfig config.Interface, globalLogger logrus.FieldLogger, eventBus event_bus.Interface) (DatabaseRepository, error) {
1212
switch pkg.DatabaseRepositoryType(appConfig.GetString("database.type")) {
1313
case pkg.DatabaseRepositoryTypeSqlite:
14-
return newSqliteRepository(appConfig, globalLogger, eventBus)
14+
return newSqliteRepository(appConfig, globalLogger, eventBus, appConfig.GetBool("database.validation_mode"))
1515
case pkg.DatabaseRepositoryTypePostgres:
1616
return newPostgresRepository(appConfig, globalLogger, eventBus)
1717
default:

backend/pkg/database/gorm_repository_graph_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph() {
6262
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
6363
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
6464
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
65+
fakeConfig.EXPECT().GetBool("database.validation_mode").Return(false).AnyTimes()
66+
fakeConfig.EXPECT().GetBool("database.encryption.enabled").Return(false).AnyTimes()
6567
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
6668
require.NoError(suite.T(), err)
6769

@@ -130,6 +132,8 @@ func (suite *RepositoryGraphTestSuite) TestGetFlattenedResourceGraph_NDJson() {
130132
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
131133
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
132134
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
135+
fakeConfig.EXPECT().GetBool("database.validation_mode").Return(false).AnyTimes()
136+
fakeConfig.EXPECT().GetBool("database.encryption.enabled").Return(false).AnyTimes()
133137
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
134138
require.NoError(suite.T(), err)
135139

backend/pkg/database/gorm_repository_query_sql_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ func (suite *RepositorySqlTestSuite) BeforeTest(suiteName, testName string) {
4646
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
4747
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
4848
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
49+
fakeConfig.EXPECT().GetBool("database.validation_mode").Return(false).AnyTimes()
50+
fakeConfig.EXPECT().GetBool("database.encryption.enabled").Return(false).AnyTimes()
4951
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
5052
require.NoError(suite.T(), err)
5153
suite.TestRepository = dbRepo

backend/pkg/database/gorm_repository_query_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ func (suite *RepositoryTestSuite) TestQueryResources_SQL() {
267267
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
268268
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
269269
fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
270+
fakeConfig.EXPECT().GetBool("database.validation_mode").Return(false).AnyTimes()
271+
fakeConfig.EXPECT().GetBool("database.encryption.enabled").Return(false).AnyTimes()
270272
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
271273
require.NoError(suite.T(), err)
272274
userModel := &models.User{

backend/pkg/database/gorm_repository_related_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func (suite *RepositoryRelatedTestSuite) SetupTest() {
4242
fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
4343
fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
4444
fakeConfig.EXPECT().GetString("log.level").Return("DEBUG").AnyTimes()
45+
fakeConfig.EXPECT().GetBool("database.validation_mode").Return(false).AnyTimes()
46+
fakeConfig.EXPECT().GetBool("database.encryption.enabled").Return(false).AnyTimes()
4547

4648
dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
4749
require.NoError(suite.T(), err, "Failed to create repository")

backend/pkg/database/gorm_repository_settings_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func (suite *RepositorySettingsTestSuite) BeforeTest(suiteName, testName string)
4040
testConfig, err := config.Create()
4141
require.NoError(suite.T(), err)
4242
testConfig.SetDefault("database.location", suite.TestDatabase.Name())
43+
testConfig.SetDefault("database.encryption.enabled", false)
4344
testConfig.SetDefault("log.level", "INFO")
4445
suite.TestConfig = testConfig
4546

0 commit comments

Comments
 (0)