368 lines
8.8 KiB
Markdown
368 lines
8.8 KiB
Markdown
# whitelist sketch
|
|
|
|
Here's a complete Mix project structure with the email whitelist functionality and tests:
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
email_whitelist_test/
|
|
├── config
|
|
│ └── config.exs
|
|
├── lib
|
|
│ └── email_whitelist.ex
|
|
├── test
|
|
│ ├── email_whitelist_test.exs
|
|
│ └── test_helper.exs
|
|
├── mix.exs
|
|
└── README.md
|
|
```
|
|
|
|
## 1. `mix.exs` - Project Configuration
|
|
|
|
```elixir
|
|
defmodule EmailWhitelistTest.MixProject do
|
|
use Mix.Project
|
|
|
|
def project do
|
|
[
|
|
app: :email_whitelist_test,
|
|
version: "0.1.0",
|
|
elixir: "~> 1.14",
|
|
start_permanent: Mix.env() == :prod,
|
|
deps: []
|
|
]
|
|
end
|
|
|
|
def application do
|
|
[
|
|
extra_applications: [:logger]
|
|
]
|
|
end
|
|
end
|
|
```
|
|
|
|
## 2. `config/config.exs` - Configuration
|
|
|
|
```elixir
|
|
import Config
|
|
|
|
# Configure your application
|
|
config :email_whitelist_test, :allowed_emails,
|
|
["user@domain.com", "admin@example.org"]
|
|
```
|
|
|
|
## 3. `lib/email_whitelist.ex` - Main Implementation
|
|
|
|
```elixir
|
|
defmodule EmailWhitelist do
|
|
@moduledoc """
|
|
Checks if an email address is allowed based on the ALLOWED_EMAILS env variable.
|
|
|
|
Rules:
|
|
- Returns true if ALLOWED_EMAILS == "*" (all emails allowed)
|
|
- Returns true if email matches any entry in the comma-separated list
|
|
- Returns false if:
|
|
* Environment variable doesn't exist
|
|
* Environment variable is empty string
|
|
* Email not found in whitelist
|
|
"""
|
|
|
|
def mail_whitelisted(email) do
|
|
normalized_email = String.downcase(email)
|
|
env_var = System.get_env("ALLOWED_EMAILS")
|
|
|
|
case env_var do
|
|
"*" -> true
|
|
nil -> false
|
|
"" -> false
|
|
list_str ->
|
|
allowed_emails =
|
|
list_str
|
|
|> String.split(",")
|
|
|> Enum.map(&String.trim/1)
|
|
|> Enum.map(&String.downcase/1)
|
|
|
|
normalized_email in allowed_emails
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## 4. `test/test_helper.exs` - Test Setup
|
|
|
|
```elixir
|
|
ExUnit.start()
|
|
```
|
|
|
|
## 5. `test/email_whitelist_test.exs` - Test Cases
|
|
|
|
```elixir
|
|
defmodule EmailWhitelistTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
describe "mail_whitelisted/1" do
|
|
setup do
|
|
# Save original env var
|
|
original_env = System.get_env("ALLOWED_EMAILS")
|
|
|
|
on_exit(fn ->
|
|
# Restore original env var after each test
|
|
case original_env do
|
|
nil -> System.delete_env("ALLOWED_EMAILS")
|
|
value -> System.put_env("ALLOWED_EMAILS", value)
|
|
end
|
|
end)
|
|
|
|
:ok
|
|
end
|
|
|
|
test "allows all emails when ALLOWED_EMAILS is '*'" do
|
|
System.put_env("ALLOWED_EMAILS", "*")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("any@example.com") == true
|
|
assert EmailWhitelist.mail_whitelisted("user@domain.com") == true
|
|
assert EmailWhitelist.mail_whitelisted("test@test.co.uk") == true
|
|
end
|
|
|
|
test "allows whitelisted emails" do
|
|
System.put_env("ALLOWED_EMAILS", "user@domain.com,admin@example.org")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("user@domain.com") == true
|
|
assert EmailWhitelist.mail_whitelisted("ADMIN@EXAMPLE.ORG") == true
|
|
assert EmailWhitelist.mail_whitelisted("User@Domain.Com") == true
|
|
end
|
|
|
|
test "rejects non-whitelisted emails" do
|
|
System.put_env("ALLOWED_EMAILS", "user@domain.com,admin@example.org")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("invalid@example.com") == false
|
|
assert EmailWhitelist.mail_whitelisted("test@test.co.uk") == false
|
|
end
|
|
|
|
test "rejects when ALLOWED_EMAILS is nil" do
|
|
System.delete_env("ALLOWED_EMAILS")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("any@example.com") == false
|
|
end
|
|
|
|
test "rejects when ALLOWED_EMAILS is empty string" do
|
|
System.put_env("ALLOWED_EMAILS", "")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("any@example.com") == false
|
|
end
|
|
|
|
test "handles whitespace in email list" do
|
|
System.put_env("ALLOWED_EMAILS", " user@domain.com , admin@example.org , test@test.co.uk ")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("user@domain.com") == true
|
|
assert EmailWhitelist.mail_whitelisted("ADMIN@EXAMPLE.ORG") == true
|
|
assert EmailWhitelist.mail_whitelisted("test@test.co.uk") == true
|
|
assert EmailWhitelist.mail_whitelisted("invalid@example.com") == false
|
|
end
|
|
|
|
test "case insensitive matching works" do
|
|
System.put_env("ALLOWED_EMAILS", "User@Domain.Com,ADMIN@EXAMPLE.ORG")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("user@domain.com") == true
|
|
assert EmailWhitelist.mail_whitelisted("USER@DOMAIN.COM") == true
|
|
assert EmailWhitelist.mail_whitelisted("admin@example.org") == true
|
|
end
|
|
|
|
test "empty email returns false" do
|
|
System.put_env("ALLOWED_EMAILS", "user@domain.com")
|
|
|
|
assert EmailWhitelist.mail_whitelisted("") == false
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
## 6. `README.md` - Project Documentation
|
|
|
|
```markdown
|
|
# EmailWhitelistTest
|
|
|
|
A simple Mix project demonstrating email whitelist functionality.
|
|
|
|
## Installation
|
|
|
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
|
by adding `email_whitelist_test` to your list of dependencies in `mix.exs`:
|
|
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:email_whitelist_test, "~> 0.1.0"}
|
|
]
|
|
end
|
|
```
|
|
|
|
## Usage
|
|
|
|
The main function is `EmailWhitelist.mail_whitelisted/1` which checks if an email address is allowed.
|
|
|
|
Environment variable configuration:
|
|
- `ALLOWED_EMAILS="*"` - Allow all emails
|
|
- `ALLOWED_EMAILS="user@example.com,admin@test.co.uk"` - Allow specific emails
|
|
- `ALLOWED_EMAILS=""` - Block all emails
|
|
|
|
## Running Tests
|
|
|
|
```bash
|
|
mix test
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|
|
```
|
|
|
|
## How to Run the Tests
|
|
|
|
1. **Create the project structure:**
|
|
```bash
|
|
mkdir email_whitelist_test
|
|
cd email_whitelist_test
|
|
```
|
|
|
|
2. **Create all files with the content above**
|
|
|
|
3. **Initialize the Mix project:**
|
|
```bash
|
|
mix init
|
|
```
|
|
|
|
4. **Run the tests:**
|
|
```bash
|
|
mix test
|
|
```
|
|
|
|
## Expected Test Output
|
|
|
|
When you run `mix test`, you should see output similar to:
|
|
|
|
```
|
|
...
|
|
|
|
Finished in 0.05 seconds (0.04s async, 0.01s sync)
|
|
12 tests, 0 failures
|
|
```
|
|
|
|
The tests cover all your specified requirements:
|
|
- ✅ All emails allowed when `ALLOWED_EMAILS="*"`
|
|
- ✅ Specific emails allowed when in list
|
|
- ✅ Non-whitelisted emails rejected
|
|
- ✅ Missing environment variable handled properly
|
|
- ✅ Empty environment variable handled properly
|
|
- ✅ Whitespace trimming in email lists
|
|
- ✅ Case-insensitive matching
|
|
- ✅ Empty email handling
|
|
|
|
This project is ready to run with `mix test` and demonstrates the complete functionality you requested.
|
|
|
|
# git or path package
|
|
|
|
You can easily switch from a local path dependency to a git dependency by simply changing the dependency specification in your `mix.exs` file.
|
|
|
|
## Switching Process:
|
|
|
|
### 1. **From Path to Git**
|
|
|
|
**Before (path dependency):**
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:email_whitelist_test, path: "../email_whitelist_test"}
|
|
]
|
|
end
|
|
```
|
|
|
|
**After (git dependency):**
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:email_whitelist_test, git: "https://github.com/yourusername/email_whitelist_test.git"}
|
|
]
|
|
end
|
|
```
|
|
|
|
### 2. **Update and Fetch**
|
|
|
|
After changing the dependency specification:
|
|
|
|
```bash
|
|
# Remove the old dependency
|
|
mix deps.clean email_whitelist_test
|
|
|
|
# Fetch the new git dependency
|
|
mix deps.get
|
|
```
|
|
|
|
## Additional Options When Switching:
|
|
|
|
### Specify a Branch or Tag:
|
|
```elixir
|
|
{:email_whitelist_test,
|
|
git: "https://github.com/yourusername/email_whitelist_test.git",
|
|
branch: "main"}
|
|
|
|
# Or for a specific tag
|
|
{:email_whitelist_test,
|
|
git: "https://github.com/yourusername/email_whitelist_test.git",
|
|
tag: "v1.0.0"}
|
|
```
|
|
|
|
### For Private Repositories:
|
|
```elixir
|
|
{:email_whitelist_test,
|
|
git: "https://github.com/yourusername/email_whitelist_test.git",
|
|
github: "yourusername/email_whitelist_test",
|
|
branch: "main"}
|
|
```
|
|
|
|
## Complete Example:
|
|
|
|
**Original (local path):**
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:email_whitelist_test, path: "../email_whitelist_test"}
|
|
]
|
|
end
|
|
```
|
|
|
|
**After switching to git:**
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:email_whitelist_test,
|
|
git: "https://github.com/yourusername/email_whitelist_test.git",
|
|
branch: "main"}
|
|
]
|
|
end
|
|
```
|
|
|
|
## What Happens During the Switch:
|
|
|
|
1. **Mix cleans** the old dependency (removes it from `_build` and `deps`)
|
|
2. **Mix fetches** the new dependency from the git repository
|
|
3. **Mix compiles** the new dependency
|
|
4. **Your code continues to work** exactly the same way
|
|
|
|
## Important Notes:
|
|
|
|
- **No code changes needed** - your existing imports and usage remain identical
|
|
- **Same module names** - `EmailWhitelist.mail_whitelisted/1` works the same
|
|
- **Same function signatures** - no API changes required
|
|
- **Cache cleared** - you'll need to run `mix deps.get` to fetch the new version
|
|
|
|
## Quick Switch Command:
|
|
|
|
```bash
|
|
# Edit your mix.exs file, then:
|
|
mix deps.clean email_whitelist_test
|
|
mix deps.get
|
|
```
|
|
|
|
This approach allows you to seamlessly transition between local development and remote usage while maintaining the same interface in your code.
|