Skip to content

Commit

Permalink
Add for Turnstile: environment variables, Context, Service
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec committed Jul 17, 2024
1 parent d805fb4 commit 1db2d1a
Show file tree
Hide file tree
Showing 19 changed files with 141 additions and 42 deletions.
2 changes: 1 addition & 1 deletion Backend.Tests/Controllers/UserControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Setup()
_userRepo = new UserRepositoryMock();
_permissionService = new PermissionServiceMock(_userRepo);
_userController = new UserController(_userRepo, _permissionService,
new EmailServiceMock(), new PasswordResetServiceMock());
new EmailServiceMock(), new PasswordResetServiceMock(), new TurnstileServiceMock());
}

private static User RandomUser()
Expand Down
13 changes: 13 additions & 0 deletions Backend.Tests/Mocks/TurnstileServiceMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Threading.Tasks;
using BackendFramework.Interfaces;

namespace Backend.Tests.Mocks
{
sealed internal class TurnstileServiceMock : ITurnstileService
{
public Task<bool> VerifyToken(string token)
{
return Task.FromResult(true);
}
}
}
19 changes: 19 additions & 0 deletions Backend/Contexts/TurnstileContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Diagnostics.CodeAnalysis;
using BackendFramework.Interfaces;
using Microsoft.Extensions.Options;

namespace BackendFramework.Contexts
{
[ExcludeFromCodeCoverage]
public class TurnstileContext : ITurnstileContext
{
public string? TurnstileSecretKey { get; }
public string? TurnstileVerifyUrl { get; }

public TurnstileContext(IOptions<Startup.Settings> options)
{
TurnstileSecretKey = options.Value.TurnstileSecretKey;
TurnstileVerifyUrl = options.Value.TurnstileVerifyUrl;
}
}
}
24 changes: 5 additions & 19 deletions Backend/Controllers/UserController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Http;
using System.Threading.Tasks;
using BackendFramework.Helper;
using BackendFramework.Interfaces;
Expand All @@ -23,16 +21,16 @@ public class UserController : Controller
private readonly IEmailService _emailService;
private readonly IPasswordResetService _passwordResetService;
private readonly IPermissionService _permissionService;

private const string TurnstileVerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
private readonly ITurnstileService _turnstileService;

public UserController(IUserRepository userRepo, IPermissionService permissionService,
IEmailService emailService, IPasswordResetService passwordResetService)
IEmailService emailService, IPasswordResetService passwordResetService, ITurnstileService turnstileService)
{
_userRepo = userRepo;
_emailService = emailService;
_passwordResetService = passwordResetService;
_permissionService = permissionService;
_turnstileService = turnstileService;
}

/// <summary> Validates a Cloudflare Turnstile token </summary>
Expand All @@ -41,21 +39,9 @@ public UserController(IUserRepository userRepo, IPermissionService permissionSer
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> ValidateTurnstile(string token)
{
var secret = Environment.GetEnvironmentVariable("TURNSTILE_SECRET_KEY");
var httpContent = new FormUrlEncodedContent(new Dictionary<string, string>{
{"response", token},
// https://developers.cloudflare.com/turnstile/troubleshooting/testing/
{"secret", secret ?? "1x0000000000000000000000000000000AA"}, // pass
//{"secret", secret ?? "2x0000000000000000000000000000000AA"}, // fail
//{"secret", secret ?? "3x0000000000000000000000000000000AA"}, // token spent
});
using var result = await new HttpClient().PostAsync(TurnstileVerifyUrl, httpContent);
var contentString = await result.Content.ReadAsStringAsync();
Console.WriteLine($"content: {contentString}");
return contentString.Contains("\"success\":true") ? Ok() : BadRequest();
return await _turnstileService.VerifyToken(token) ? Ok() : BadRequest();
}


/// <summary> Sends a password reset request </summary>
[AllowAnonymous]
[HttpPost("forgot", Name = "ResetPasswordRequest")]
Expand Down
8 changes: 8 additions & 0 deletions Backend/Interfaces/ITurnstileContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace BackendFramework.Interfaces
{
public interface ITurnstileContext
{
string? TurnstileSecretKey { get; }
string? TurnstileVerifyUrl { get; }
}
}
9 changes: 9 additions & 0 deletions Backend/Interfaces/ITurnstileService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Threading.Tasks;

namespace BackendFramework.Interfaces
{
public interface ITurnstileService
{
Task<bool> VerifyToken(string token);
}
}
10 changes: 8 additions & 2 deletions Backend/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"launchBrowser": true,
"launchUrl": "v1",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"COMBINE_JWT_SECRET_KEY": "0123456789abcdefghijklmnopqrstuvwxyz",
"TURNSTILE_SECRET_KEY": "1x0000000000000000000000000000000AA",
"TURNSTILE_VERIFY_URL": "challenges.cloudflare.com/turnstile/v0/siteverify"
}
},
"BackendFramework": {
Expand All @@ -23,7 +26,10 @@
"launchUrl": "v1/",
"environmentVariables": {
"Key": "Value",
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"COMBINE_JWT_SECRET_KEY": "0123456789abcdefghijklmnopqrstuvwxyz",
"TURNSTILE_SECRET_KEY": "1x0000000000000000000000000000000AA",
"TURNSTILE_VERIFY_URL": "challenges.cloudflare.com/turnstile/v0/siteverify"
},
"applicationUrl": "http://localhost:5000",
"hotReloadProfile": "aspnetcore"
Expand Down
38 changes: 38 additions & 0 deletions Backend/Services/TurnstileService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Threading.Tasks;
using BackendFramework.Interfaces;

namespace BackendFramework.Services
{
[ExcludeFromCodeCoverage]
public class TurnstileService : ITurnstileService
{
private readonly ITurnstileContext _turnstileContext;

public TurnstileService(ITurnstileContext turnstileContext)
{
_turnstileContext = turnstileContext;
}

public async Task<bool> VerifyToken(string token)
{
var secret = _turnstileContext.TurnstileSecretKey;
var verifyUrl = _turnstileContext.TurnstileVerifyUrl;
if (string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(verifyUrl))
{
return false;
}
var httpContent = new FormUrlEncodedContent(new Dictionary<string, string>{
{"response", token},
{"secret", secret},
});
using var result = await new HttpClient().PostAsync(verifyUrl, httpContent);
var contentString = await result.Content.ReadAsStringAsync();
Console.WriteLine($"content: {contentString}");
return contentString.Contains("\"success\":true");
}
}
}
18 changes: 16 additions & 2 deletions Backend/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class Settings
public string? SmtpAddress { get; set; }
public string? SmtpFrom { get; set; }
public int PassResetExpireTime { get; set; }
public string? TurnstileSecretKey { get; set; }
public string? TurnstileVerifyUrl { get; set; }

public Settings()
{
Expand Down Expand Up @@ -179,12 +181,20 @@ public void ConfigureServices(IServiceCollection services)
null,
emailServiceFailureMessage);

// Should we add a COMBINE_TURNSTILE_SECRET_KEY check?

options.PassResetExpireTime = int.Parse(CheckedEnvironmentVariable(
"COMBINE_PASSWORD_RESET_EXPIRE_TIME",
Settings.DefaultPasswordResetExpireTime.ToString(),
$"Using default value: {Settings.DefaultPasswordResetExpireTime}")!);

const string turnstileFailureMessage = "Turnstile verification will not be available.";
options.TurnstileSecretKey = CheckedEnvironmentVariable(
"TURNSTILE_SECRET_KEY",
null,
turnstileFailureMessage);
options.TurnstileVerifyUrl = CheckedEnvironmentVariable(
"TURNSTILE_VERIFY_URL",
null,
turnstileFailureMessage);
});

// Register concrete types for dependency injection
Expand Down Expand Up @@ -231,6 +241,10 @@ public void ConfigureServices(IServiceCollection services)
// Statistics types
services.AddSingleton<IStatisticsService, StatisticsService>();

// Turnstile types
services.AddTransient<ITurnstileContext, TurnstileContext>();
services.AddTransient<ITurnstileService, TurnstileService>();

// User types
services.AddTransient<IUserContext, UserContext>();
services.AddTransient<IUserRepository, UserRepository>();
Expand Down
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,9 @@ A rapid word collection tool. See the [User Guide](https://sillsdev.github.io/Th

### Prepare the Environment

1. Set the environment variable `COMBINE_JWT_SECRET_KEY` to a string **containing at least 32 characters**, such as
_This is a secret key that is longer_. Set it in your `.profile` (Linux or Mac 10.14-), your `.zprofile` (Mac
10.15+), or the _System_ app (Windows).
2. If you want the email services to work you will need to set the following environment variables. These values must be
kept secret, so ask your email administrator to supply them.
1. If you want the email services to work you will need to set the following environment variables. These values must be
kept secret, so ask your email administrator to supply them. Set them in your `.profile` (Linux or Mac 10.14-), your
`.zprofile` (Mac 10.15+), or the _System_ app (Windows).

- `COMBINE_SMTP_SERVER`
- `COMBINE_SMTP_PORT`
Expand All @@ -155,16 +153,16 @@ A rapid word collection tool. See the [User Guide](https://sillsdev.github.io/Th
- `COMBINE_SMTP_ADDRESS`
- `COMBINE_SMTP_FROM`

3. _(Optional)_ To opt in to segment.com analytics to test the analytics during development:
2. _(Optional)_ To opt in to segment.com analytics to test the analytics during development:

```bash
# For Windows, use `copy`.
cp .env.local.template .env.local
```

4. Run `npm start` from the project directory to install dependencies and start the project.
3. Run `npm start` from the project directory to install dependencies and start the project.

5. Consult our [C#](docs/style_guide/c_sharp_style_guide.md) and [TypeScript](docs/style_guide/ts_style_guide.md) style
4. Consult our [C#](docs/style_guide/c_sharp_style_guide.md) and [TypeScript](docs/style_guide/ts_style_guide.md) style
guides for best coding practices in this project.

[chocolatey]: https://chocolatey.org/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ data:
COMBINE_SMTP_FROM: {{ .Values.combineSmtpFrom | quote }}
COMBINE_SMTP_PORT: {{ .Values.combineSmtpPort | quote }}
COMBINE_SMTP_SERVER: {{ .Values.combineSmtpServer | quote }}
TURNSTILE_VERIFY_URL: {{ .Values.turnstileVerifyUrl | quote }}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ data:
COMBINE_JWT_SECRET_KEY: {{ .Values.global.combineJwtSecretKey | b64enc }}
COMBINE_SMTP_USERNAME: {{ .Values.global.combineSmtpUsername | b64enc }}
COMBINE_SMTP_PASSWORD: {{ .Values.global.combineSmtpPassword | b64enc }}
COMBINE_TURNSTILE_SECRET_KEY: {{ .Values.global.combineTurnstileSecretKey | b64enc }}
TURNSTILE_SECRET_KEY: {{ .Values.global.turnstileSecretKey | b64enc }}
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,16 @@ spec:
secretKeyRef:
key: COMBINE_SMTP_USERNAME
name: env-backend-secrets
- name: COMBINE_TURNSTILE_SECRET_KEY
- name: TURNSTILE_SECRET_KEY
valueFrom:
secretKeyRef:
key: COMBINE_TURNSTILE_SECRET_KEY
key: TURNSTILE_SECRET_KEY
name: env-backend-secrets
- name: TURNSTILE_VERIFY_URL
valueFrom:
configMapKeyRef:
key: TURNSTILE_VERIFY_URL
name: env-backend
ports:
- containerPort: 5000
resources:
Expand Down
3 changes: 3 additions & 0 deletions deploy/helm/thecombine/charts/backend/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ global:
imageTag: "latest"
# Define the image registry to use (may be blank for local images)
imageRegistry: ""
# Cloudflare Turnstile verification
turnstileSecretKey: "Override"

persistentVolumeSize: 32Gi
combinePasswordResetTime: 15
Expand All @@ -35,3 +37,4 @@ combineSmtpFrom: "The Combine"
combineSmtpPort: 587
combineSmtpServer: "email-smtp.us-east-1.amazonaws.com"
imageName: combine_backend
turnstileVerifyUrl: "challenges.cloudflare.com/turnstile/v0/siteverify"
3 changes: 2 additions & 1 deletion deploy/helm/thecombine/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ global:
combineJwtSecretKey: "Override"
combineSmtpUsername: "Override"
combineSmtpPassword: "Override"
combineTurnstileSecretKey: "Override"
offline: false
# Local Storage for fonts
fontStorageAccessMode: "ReadWriteOnce"
Expand All @@ -39,6 +38,8 @@ global:
pullSecretName: aws-login-credentials
# Update strategy should be "Recreate" or "Rolling Update"
updateStrategy: Recreate
# Cloudflare Turnstile verification
turnstileSecretKey: "Override"

includeResourceLimits: false

Expand Down
2 changes: 0 additions & 2 deletions deploy/scripts/install-combine.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ set-combine-env () {
# Generate JWT Secret Key
COMBINE_JWT_SECRET_KEY=`LC_ALL=C tr -dc 'A-Za-z0-9*\-_@!' </dev/urandom | head -c 64; echo`
# Collect values from user
read -p "Enter COMBINE_TURNSTILE_SECRET_KEY: " COMBINE_TURNSTILE_SECRET_KEY
read -p "Enter AWS_ACCESS_KEY_ID: " AWS_ACCESS_KEY_ID
read -p "Enter AWS_SECRET_ACCESS_KEY: " AWS_SECRET_ACCESS_KEY
# write collected values and static values to config file
cat <<.EOF > ${CONFIG_DIR}/env
export COMBINE_JWT_SECRET_KEY="${COMBINE_JWT_SECRET_KEY}"
export COMBINE_TURNSTILE_SECRET_KEY="${COMBINE_TURNSTILE_SECRET_KEY}"
export AWS_DEFAULT_REGION="us-east-1"
export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}"
export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"
Expand Down
4 changes: 2 additions & 2 deletions deploy/scripts/setup_files/combine_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ charts:
env_var: COMBINE_SMTP_USERNAME
- config_item: combineSmtpPassword
env_var: COMBINE_SMTP_PASSWORD
- config_item: combineTurnstileSecretKey
env_var: COMBINE_TURNSTILE_SECRET_KEY
- config_item: turnstileSecretKey
env_var: TURNSTILE_SECRET_KEY
create-admin-user:
namespace: thecombine
install_langs: false
Expand Down
2 changes: 1 addition & 1 deletion docs/deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,10 @@ The setup scripts require the following environment variables to be set:
- COMBINE_JWT_SECRET_KEY
- COMBINE_SMTP_USERNAME
- COMBINE_SMTP_PASSWORD
- COMBINE_TURNSTILE_SECRET_KEY
- COMBINE_ADMIN_USERNAME
- COMBINE_ADMIN_PASSWORD
- COMBINE_ADMIN_EMAIL
- TURNSTILE_SECRET_KEY

You may also set the KUBECONFIG environment variable to the location of the `kubectl` configuration file. This is not
necessary if the configuration file is at `${HOME}/.kube/config`.
Expand Down
2 changes: 1 addition & 1 deletion public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"backToLogin": "Back To Login"
},
"turnstile": {
"error": "Cloudflare Turnstile verification failed or expired. Please refresh the page to try again."
"error": "Page verification failed or expired. Please refresh the page."
},
"speakerMenu": {
"none": "No speakers in the project. To attach a speaker to audio recordings, please talk to a project administrator.",
Expand Down

0 comments on commit 1db2d1a

Please sign in to comment.