mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-09-10 18:55:57 +03:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a7a479623c | ||
|
83dff9ae6e | ||
|
6b2cc5a3ee | ||
|
5247e0d773 | ||
|
05b308b8b4 | ||
|
9621278fca | ||
|
570d6c8bf9 |
@@ -118,6 +118,14 @@
|
|||||||
## even if SIGNUPS_ALLOWED is set to false
|
## even if SIGNUPS_ALLOWED is set to false
|
||||||
# SIGNUPS_DOMAINS_WHITELIST=example.com,example.net,example.org
|
# SIGNUPS_DOMAINS_WHITELIST=example.com,example.net,example.org
|
||||||
|
|
||||||
|
## Controls which users can create new orgs.
|
||||||
|
## Blank or 'all' means all users can create orgs (this is the default):
|
||||||
|
# ORG_CREATION_USERS=
|
||||||
|
## 'none' means no users can create orgs:
|
||||||
|
# ORG_CREATION_USERS=none
|
||||||
|
## A comma-separated list means only those users can create orgs:
|
||||||
|
# ORG_CREATION_USERS=admin1@example.com,admin2@example.com
|
||||||
|
|
||||||
## Token for the admin interface, preferably use a long random string
|
## Token for the admin interface, preferably use a long random string
|
||||||
## One option is to use 'openssl rand -base64 48'
|
## One option is to use 'openssl rand -base64 48'
|
||||||
## If not set, the admin panel is disabled
|
## If not set, the admin panel is disabled
|
||||||
|
@@ -41,11 +41,16 @@ if [[ "${DOCKER_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|||||||
# auto-select that image on Armv6 platforms like Raspberry Pi 1 and Zero
|
# auto-select that image on Armv6 platforms like Raspberry Pi 1 and Zero
|
||||||
# (https://github.com/moby/moby/issues/41017).
|
# (https://github.com/moby/moby/issues/41017).
|
||||||
#
|
#
|
||||||
|
# Add this tag only for the SQLite image, as the MySQL and PostgreSQL
|
||||||
|
# builds don't currently work on non-amd64 arches.
|
||||||
|
#
|
||||||
# TODO: Also add an `alpine-arm32v6` tag if multi-arch support for
|
# TODO: Also add an `alpine-arm32v6` tag if multi-arch support for
|
||||||
# Alpine-based bitwarden_rs images is implemented before this Docker
|
# Alpine-based bitwarden_rs images is implemented before this Docker
|
||||||
# issue is fixed.
|
# issue is fixed.
|
||||||
docker tag "${DOCKER_REPO}:${DOCKER_TAG}-arm32v6" "${DOCKER_REPO}:latest-arm32v6"
|
if [[ ${DOCKER_REPO} == *server ]]; then
|
||||||
docker push "${DOCKER_REPO}:latest-arm32v6"
|
docker tag "${DOCKER_REPO}:${DOCKER_TAG}-arm32v6" "${DOCKER_REPO}:latest-arm32v6"
|
||||||
|
docker push "${DOCKER_REPO}:latest-arm32v6"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@@ -76,6 +76,10 @@ struct NewCollectionData {
|
|||||||
|
|
||||||
#[post("/organizations", data = "<data>")]
|
#[post("/organizations", data = "<data>")]
|
||||||
fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn) -> JsonResult {
|
fn create_organization(headers: Headers, data: JsonUpcase<OrgData>, conn: DbConn) -> JsonResult {
|
||||||
|
if !CONFIG.is_org_creation_allowed(&headers.user.email) {
|
||||||
|
err!("User not allowed to create organizations")
|
||||||
|
}
|
||||||
|
|
||||||
let data: OrgData = data.into_inner().data;
|
let data: OrgData = data.into_inner().data;
|
||||||
|
|
||||||
let org = Organization::new(data.Name, data.BillingEmail);
|
let org = Organization::new(data.Name, data.BillingEmail);
|
||||||
|
@@ -115,6 +115,7 @@ macro_rules! make_config {
|
|||||||
config.domain_set = _domain_set;
|
config.domain_set = _domain_set;
|
||||||
|
|
||||||
config.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase();
|
config.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase();
|
||||||
|
config.org_creation_users = config.org_creation_users.trim().to_lowercase();
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
@@ -276,6 +277,9 @@ make_config! {
|
|||||||
signups_verify_resend_limit: u32, true, def, 6;
|
signups_verify_resend_limit: u32, true, def, 6;
|
||||||
/// Email domain whitelist |> Allow signups only from this list of comma-separated domains, even when signups are otherwise disabled
|
/// Email domain whitelist |> Allow signups only from this list of comma-separated domains, even when signups are otherwise disabled
|
||||||
signups_domains_whitelist: String, true, def, "".to_string();
|
signups_domains_whitelist: String, true, def, "".to_string();
|
||||||
|
/// Org creation users |> Allow org creation only by this list of comma-separated user emails.
|
||||||
|
/// Blank or 'all' means all users can create orgs; 'none' means no users can create orgs.
|
||||||
|
org_creation_users: String, true, def, "".to_string();
|
||||||
/// Allow invitations |> Controls whether users can be invited by organization admins, even when signups are otherwise disabled
|
/// Allow invitations |> Controls whether users can be invited by organization admins, even when signups are otherwise disabled
|
||||||
invitations_allowed: bool, true, def, true;
|
invitations_allowed: bool, true, def, true;
|
||||||
/// Password iterations |> Number of server-side passwords hashing iterations.
|
/// Password iterations |> Number of server-side passwords hashing iterations.
|
||||||
@@ -442,6 +446,13 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
|
|||||||
err!("`SIGNUPS_DOMAINS_WHITELIST` contains empty tokens");
|
err!("`SIGNUPS_DOMAINS_WHITELIST` contains empty tokens");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let org_creation_users = cfg.org_creation_users.trim().to_lowercase();
|
||||||
|
if !(org_creation_users.is_empty() || org_creation_users == "all" || org_creation_users == "none") {
|
||||||
|
if org_creation_users.split(',').any(|u| !u.contains('@')) {
|
||||||
|
err!("`ORG_CREATION_USERS` contains invalid email addresses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref token) = cfg.admin_token {
|
if let Some(ref token) = cfg.admin_token {
|
||||||
if token.trim().is_empty() && !cfg.disable_admin_token {
|
if token.trim().is_empty() && !cfg.disable_admin_token {
|
||||||
println!("[WARNING] `ADMIN_TOKEN` is enabled but has an empty value, so the admin page will be disabled.");
|
println!("[WARNING] `ADMIN_TOKEN` is enabled but has an empty value, so the admin page will be disabled.");
|
||||||
@@ -592,6 +603,19 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests whether the specified user is allowed to create an organization.
|
||||||
|
pub fn is_org_creation_allowed(&self, email: &str) -> bool {
|
||||||
|
let users = self.org_creation_users();
|
||||||
|
if users == "" || users == "all" {
|
||||||
|
true
|
||||||
|
} else if users == "none" {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
let email = email.to_lowercase();
|
||||||
|
users.split(',').any(|u| u.trim() == email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delete_user_config(&self) -> Result<(), Error> {
|
pub fn delete_user_config(&self) -> Result<(), Error> {
|
||||||
crate::util::delete_file(&CONFIG_FILE)?;
|
crate::util::delete_file(&CONFIG_FILE)?;
|
||||||
|
|
||||||
|
@@ -39,8 +39,7 @@
|
|||||||
"Type": 1,
|
"Type": 1,
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"apple.com",
|
"apple.com",
|
||||||
"icloud.com",
|
"icloud.com"
|
||||||
"tv.apple.com"
|
|
||||||
],
|
],
|
||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
@@ -106,6 +105,7 @@
|
|||||||
"passport.net",
|
"passport.net",
|
||||||
"windows.com",
|
"windows.com",
|
||||||
"microsoftonline.com",
|
"microsoftonline.com",
|
||||||
|
"office.com",
|
||||||
"office365.com",
|
"office365.com",
|
||||||
"microsoftstore.com",
|
"microsoftstore.com",
|
||||||
"xbox.com",
|
"xbox.com",
|
||||||
@@ -193,7 +193,12 @@
|
|||||||
"amazon.it",
|
"amazon.it",
|
||||||
"amazon.com.au",
|
"amazon.com.au",
|
||||||
"amazon.co.nz",
|
"amazon.co.nz",
|
||||||
"amazon.in"
|
"amazon.in",
|
||||||
|
"amazon.com.mx",
|
||||||
|
"amazon.nl",
|
||||||
|
"amazon.sg",
|
||||||
|
"amazon.com.tr",
|
||||||
|
"amazon.ae"
|
||||||
],
|
],
|
||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
@@ -386,8 +391,7 @@
|
|||||||
"alibaba.com",
|
"alibaba.com",
|
||||||
"aliexpress.com",
|
"aliexpress.com",
|
||||||
"aliyun.com",
|
"aliyun.com",
|
||||||
"net.cn",
|
"net.cn"
|
||||||
"www.net.cn"
|
|
||||||
],
|
],
|
||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
@@ -717,41 +721,27 @@
|
|||||||
"eventbrite.ca",
|
"eventbrite.ca",
|
||||||
"eventbrite.ch",
|
"eventbrite.ch",
|
||||||
"eventbrite.cl",
|
"eventbrite.cl",
|
||||||
"eventbrite.co.id",
|
"eventbrite.co",
|
||||||
"eventbrite.co.in",
|
|
||||||
"eventbrite.co.kr",
|
|
||||||
"eventbrite.co.nz",
|
"eventbrite.co.nz",
|
||||||
"eventbrite.co.uk",
|
"eventbrite.co.uk",
|
||||||
"eventbrite.co.ve",
|
|
||||||
"eventbrite.com",
|
"eventbrite.com",
|
||||||
|
"eventbrite.com.ar",
|
||||||
"eventbrite.com.au",
|
"eventbrite.com.au",
|
||||||
"eventbrite.com.bo",
|
|
||||||
"eventbrite.com.br",
|
"eventbrite.com.br",
|
||||||
"eventbrite.com.co",
|
"eventbrite.com.mx",
|
||||||
"eventbrite.com.hk",
|
|
||||||
"eventbrite.com.hn",
|
|
||||||
"eventbrite.com.pe",
|
"eventbrite.com.pe",
|
||||||
"eventbrite.com.sg",
|
|
||||||
"eventbrite.com.tr",
|
|
||||||
"eventbrite.com.tw",
|
|
||||||
"eventbrite.cz",
|
|
||||||
"eventbrite.de",
|
"eventbrite.de",
|
||||||
"eventbrite.dk",
|
"eventbrite.dk",
|
||||||
|
"eventbrite.es",
|
||||||
"eventbrite.fi",
|
"eventbrite.fi",
|
||||||
"eventbrite.fr",
|
"eventbrite.fr",
|
||||||
"eventbrite.gy",
|
"eventbrite.hk",
|
||||||
"eventbrite.hu",
|
|
||||||
"eventbrite.ie",
|
"eventbrite.ie",
|
||||||
"eventbrite.is",
|
|
||||||
"eventbrite.it",
|
"eventbrite.it",
|
||||||
"eventbrite.jp",
|
|
||||||
"eventbrite.mx",
|
|
||||||
"eventbrite.nl",
|
"eventbrite.nl",
|
||||||
"eventbrite.no",
|
|
||||||
"eventbrite.pl",
|
|
||||||
"eventbrite.pt",
|
"eventbrite.pt",
|
||||||
"eventbrite.ru",
|
"eventbrite.se",
|
||||||
"eventbrite.se"
|
"eventbrite.sg"
|
||||||
],
|
],
|
||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
@@ -769,15 +759,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": 75,
|
"Type": 75,
|
||||||
"Domains": [
|
|
||||||
"netcup.de",
|
|
||||||
"netcup.eu",
|
|
||||||
"customercontrolpanel.de"
|
|
||||||
],
|
|
||||||
"Excluded": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Type": 76,
|
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"docusign.com",
|
"docusign.com",
|
||||||
"docusign.net"
|
"docusign.net"
|
||||||
@@ -785,7 +766,7 @@
|
|||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": 77,
|
"Type": 76,
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"envato.com",
|
"envato.com",
|
||||||
"themeforest.net",
|
"themeforest.net",
|
||||||
@@ -799,7 +780,7 @@
|
|||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": 78,
|
"Type": 77,
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"x10hosting.com",
|
"x10hosting.com",
|
||||||
"x10premium.com"
|
"x10premium.com"
|
||||||
@@ -807,7 +788,7 @@
|
|||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": 79,
|
"Type": 78,
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"dnsomatic.com",
|
"dnsomatic.com",
|
||||||
"opendns.com",
|
"opendns.com",
|
||||||
@@ -816,7 +797,7 @@
|
|||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": 80,
|
"Type": 79,
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"cagreatamerica.com",
|
"cagreatamerica.com",
|
||||||
"canadaswonderland.com",
|
"canadaswonderland.com",
|
||||||
@@ -835,11 +816,19 @@
|
|||||||
"Excluded": false
|
"Excluded": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": 81,
|
"Type": 80,
|
||||||
"Domains": [
|
"Domains": [
|
||||||
"ubnt.com",
|
"ubnt.com",
|
||||||
"ui.com"
|
"ui.com"
|
||||||
],
|
],
|
||||||
"Excluded": false
|
"Excluded": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": 81,
|
||||||
|
"Domains": [
|
||||||
|
"discordapp.com",
|
||||||
|
"discord.com"
|
||||||
|
],
|
||||||
|
"Excluded": false
|
||||||
}
|
}
|
||||||
]
|
]
|
80
tools/global_domains.py
Executable file
80
tools/global_domains.py
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# This script generates a global equivalent domains JSON file from
|
||||||
|
# the upstream Bitwarden source repo.
|
||||||
|
#
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("usage: %s <OUTPUT-FILE>" % sys.argv[0])
|
||||||
|
print()
|
||||||
|
print("This script generates a global equivalent domains JSON file from")
|
||||||
|
print("the upstream Bitwarden source repo.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
OUTPUT_FILE = sys.argv[1]
|
||||||
|
|
||||||
|
BASE_URL = 'https://github.com/bitwarden/server/raw/master'
|
||||||
|
ENUMS_URL = '%s/src/Core/Enums/GlobalEquivalentDomainsType.cs' % BASE_URL
|
||||||
|
DOMAIN_LISTS_URL = '%s/src/Core/Utilities/StaticStore.cs' % BASE_URL
|
||||||
|
|
||||||
|
# Enum lines look like:
|
||||||
|
#
|
||||||
|
# EnumName0 = 0,
|
||||||
|
# EnumName1 = 1,
|
||||||
|
#
|
||||||
|
ENUM_RE = re.compile(
|
||||||
|
r'\s*' # Leading whitespace (optional).
|
||||||
|
r'([_0-9a-zA-Z]+)' # Enum name (capture group 1).
|
||||||
|
r'\s*=\s*' # '=' with optional surrounding whitespace.
|
||||||
|
r'([0-9]+)' # Enum value (capture group 2).
|
||||||
|
)
|
||||||
|
|
||||||
|
# Global domains lines look like:
|
||||||
|
#
|
||||||
|
# GlobalDomains.Add(GlobalEquivalentDomainsType.EnumName, new List<string> { "x.com", "y.com" });
|
||||||
|
#
|
||||||
|
DOMAIN_LIST_RE = re.compile(
|
||||||
|
r'\s*' # Leading whitespace (optional).
|
||||||
|
r'GlobalDomains\.Add\(GlobalEquivalentDomainsType\.'
|
||||||
|
r'([_0-9a-zA-Z]+)' # Enum name (capture group 1).
|
||||||
|
r'\s*,\s*new List<string>\s*{'
|
||||||
|
r'([^}]+)' # Domain list (capture group 2).
|
||||||
|
r'}\);'
|
||||||
|
)
|
||||||
|
|
||||||
|
enums = dict()
|
||||||
|
domain_lists = OrderedDict()
|
||||||
|
|
||||||
|
# Read in the enum names and values.
|
||||||
|
with urllib.request.urlopen(ENUMS_URL) as response:
|
||||||
|
for ln in response.read().decode('utf-8').split('\n'):
|
||||||
|
m = ENUM_RE.match(ln)
|
||||||
|
if m:
|
||||||
|
enums[m.group(1)] = int(m.group(2))
|
||||||
|
|
||||||
|
# Read in the domain lists.
|
||||||
|
with urllib.request.urlopen(DOMAIN_LISTS_URL) as response:
|
||||||
|
for ln in response.read().decode('utf-8').split('\n'):
|
||||||
|
m = DOMAIN_LIST_RE.match(ln)
|
||||||
|
if m:
|
||||||
|
# Strip double quotes and extraneous spaces in each domain.
|
||||||
|
domain_lists[m.group(1)] = [d.strip(' "') for d in m.group(2).split(",")]
|
||||||
|
|
||||||
|
# Build the global domains data structure.
|
||||||
|
global_domains = []
|
||||||
|
for name, domain_list in domain_lists.items():
|
||||||
|
entry = OrderedDict()
|
||||||
|
entry["Type"] = enums[name]
|
||||||
|
entry["Domains"] = domain_list
|
||||||
|
entry["Excluded"] = False
|
||||||
|
global_domains.append(entry)
|
||||||
|
|
||||||
|
# Write out the global domains JSON file.
|
||||||
|
with open(OUTPUT_FILE, 'w') as f:
|
||||||
|
json.dump(global_domains, f, indent=2)
|
Reference in New Issue
Block a user