diff --git a/orchestra/contrib/systemusers/forms.py b/orchestra/contrib/systemusers/forms.py
index ab0b8c49..c22d24c0 100644
--- a/orchestra/contrib/systemusers/forms.py
+++ b/orchestra/contrib/systemusers/forms.py
@@ -6,7 +6,7 @@ from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from orchestra.forms import UserCreationForm, UserChangeForm
-from orchestra.contrib.webapps.settings import WEBAPP_NEW_SERVERS
+from orchestra.settings import NEW_SERVERS
from . import settings
from .models import SystemUser
@@ -176,7 +176,7 @@ class WebappUserFormMixin(object):
if not self.instance.pk:
server = self.cleaned_data.get('target_server')
if server:
- if server.name not in WEBAPP_NEW_SERVERS:
+ if server.name not in NEW_SERVERS:
self.add_error("target_server", _(f"{server} does not belong to the new servers"))
return self.cleaned_data
diff --git a/orchestra/contrib/webapps/admin.py b/orchestra/contrib/webapps/admin.py
index 09422085..24bd4948 100644
--- a/orchestra/contrib/webapps/admin.py
+++ b/orchestra/contrib/webapps/admin.py
@@ -109,7 +109,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
def save_model(self, request, obj, form, change):
if not change:
- user = form.cleaned_data['username']
+ user = form.cleaned_data.get('username')
if user:
user = WebappUsers(
username=form.cleaned_data['username'],
diff --git a/orchestra/contrib/webapps/backends/__init__.py b/orchestra/contrib/webapps/backends/__init__.py
index 2e120fc0..0beb18a9 100644
--- a/orchestra/contrib/webapps/backends/__init__.py
+++ b/orchestra/contrib/webapps/backends/__init__.py
@@ -1,8 +1,8 @@
import pkgutil
import textwrap
-
+from django.template import Template, Context
from .. import settings
-
+from orchestra.settings import NEW_SERVERS
class WebAppServiceMixin(object):
model = 'webapps.WebApp'
@@ -19,6 +19,7 @@ class WebAppServiceMixin(object):
CREATED=0
if [[ ! -e %(app_path)s ]]; then
mkdir -p %(app_path)s
+ chown %(sftpuser)s:%(sftpuser)s %(app_path)s
CREATED=1
elif [[ -z $( ls -A %(app_path)s ) ]]; then
CREATED=1
@@ -38,6 +39,15 @@ class WebAppServiceMixin(object):
def set_under_construction(self, context):
if context['under_construction_path']:
+ # cambios de permisos en servidores nuevos
+ perms = Template(textwrap.dedent("""\
+ {% if sftpuser %}
+ chown -R {{ sftpuser }}:{{ sftpuser }} {{ app_path }}/* {% else %}
+ chown -R {{ user }}:{{ group }} {{ app_path }}/*
+ {% endif %}
+ """
+ ))
+ context.update({'perms' : perms.render(Context(context))})
self.append(textwrap.dedent("""
# Set under construction if needed
if [[ $CREATED == 1 && ! $(ls -A %(app_path)s | head -n1) ]]; then
@@ -46,11 +56,11 @@ class WebAppServiceMixin(object):
sleep 2
if [[ ! $(ls -A %(app_path)s | head -n1) ]]; then
cp -r %(under_construction_path)s %(app_path)s
- chown -R %(user)s:%(group)s %(app_path)s/*
+ %(perms)s
fi' &> /dev/null &
fi""") % context
)
-
+
def delete_webapp_dir(self, context):
if context['deleted_app_path']:
self.append(textwrap.dedent("""\
@@ -68,8 +78,8 @@ class WebAppServiceMixin(object):
def get_context(self, webapp):
context = webapp.type_instance.get_directive_context()
context.update({
- 'user': webapp.sftpuser.username if webapp.target_server.name in settings.WEBAPP_NEW_SERVERS else webapp.get_username(),
- 'group': webapp.sftpuser.username if webapp.target_server.name in settings.WEBAPP_NEW_SERVERS else webapp.get_groupname(),
+ 'user': webapp.get_username(),
+ 'group': webapp.get_groupname(),
'app_name': webapp.name,
'app_type': webapp.type,
'app_path': webapp.get_path(),
@@ -77,6 +87,7 @@ class WebAppServiceMixin(object):
'under_construction_path': settings.WEBAPPS_UNDER_CONSTRUCTION_PATH,
'is_mounted': webapp.content_set.exists(),
'target_server': webapp.target_server,
+ 'sftpuser' : webapp.sftpuser.username if webapp.target_server.name in NEW_SERVERS else None
})
context['deleted_app_path'] = settings.WEBAPPS_MOVE_ON_DELETE_PATH % context
return context
diff --git a/orchestra/contrib/webapps/backends/php.py b/orchestra/contrib/webapps/backends/php.py
index 7478fc41..04018dba 100644
--- a/orchestra/contrib/webapps/backends/php.py
+++ b/orchestra/contrib/webapps/backends/php.py
@@ -5,6 +5,7 @@ from collections import OrderedDict
from django.template import Template, Context
from django.utils.translation import gettext_lazy as _
+from orchestra.settings import NEW_SERVERS
from orchestra.contrib.orchestration import ServiceController
from . import WebAppServiceMixin
@@ -34,7 +35,12 @@ class PHPController(WebAppServiceMixin, ServiceController):
def save(self, webapp):
self.delete_old_config(webapp)
context = self.get_context(webapp)
- self.create_webapp_dir(context)
+
+ if context.get('target_server').name in NEW_SERVERS:
+ self.check_webapp_dir(context)
+ else:
+ self.create_webapp_dir(context)
+
if webapp.type_instance.is_fpm:
self.save_fpm(webapp, context)
elif webapp.type_instance.is_fcgid:
@@ -122,11 +128,10 @@ class PHPController(WebAppServiceMixin, ServiceController):
def delete(self, webapp):
context = self.get_context(webapp)
self.delete_old_config(webapp)
-# if webapp.type_instance.is_fpm:
-# self.delete_fpm(webapp, context)
-# elif webapp.type_instance.is_fcgid:
-# self.delete_fcgid(webapp, context)
- self.delete_webapp_dir(context)
+ if context.get('target_server').name in NEW_SERVERS:
+ webapp.sftpuser.delete()
+ else:
+ self.delete_webapp_dir(context)
def has_sibilings(self, webapp, context):
return type(webapp).objects.filter(
@@ -229,12 +234,21 @@ class PHPController(WebAppServiceMixin, ServiceController):
fpm_config = Template(textwrap.dedent("""\
;; {{ banner }}
[{{ user }}-{{app_name}}]
+ {% if sftpuser %}
+ user = {{ sftpuser }}
+ group = {{ sftpuser }}
+
+ listen = {{ fpm_listen | safe }}
+ listen.owner = root
+ listen.group = {{ sftpuser }}
+ {% else %}
user = {{ user }}
group = {{ group }}
listen = {{ fpm_listen | safe }}
listen.owner = {{ user }}
listen.group = {{ group }}
+ {% endif %}
pm = ondemand
pm.max_requests = {{ max_requests }}
@@ -313,6 +327,7 @@ class PHPController(WebAppServiceMixin, ServiceController):
context = super().get_context(webapp)
context.update({
'max_requests': settings.WEBAPPS_PHP_MAX_REQUESTS,
+ 'target_server': webapp.target_server,
})
self.update_fpm_context(webapp, context)
self.update_fcgid_context(webapp, context)
diff --git a/orchestra/contrib/webapps/backends/static.py b/orchestra/contrib/webapps/backends/static.py
index c2f2cb5e..ea2061a0 100644
--- a/orchestra/contrib/webapps/backends/static.py
+++ b/orchestra/contrib/webapps/backends/static.py
@@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController
from . import WebAppServiceMixin
-from ..settings import WEBAPP_NEW_SERVERS
+from orchestra.settings import NEW_SERVERS
class StaticController(WebAppServiceMixin, ServiceController):
"""
@@ -15,7 +15,7 @@ class StaticController(WebAppServiceMixin, ServiceController):
def save(self, webapp):
context = self.get_context(webapp)
- if context.get('target_server').name in WEBAPP_NEW_SERVERS:
+ if context.get('target_server').name in NEW_SERVERS:
self.check_webapp_dir(context)
self.set_under_construction(context)
else:
@@ -24,7 +24,7 @@ class StaticController(WebAppServiceMixin, ServiceController):
def delete(self, webapp):
context = self.get_context(webapp)
- if context.get('target_server').name in WEBAPP_NEW_SERVERS:
+ if context.get('target_server').name in NEW_SERVERS:
webapp.sftpuser.delete()
else:
self.delete_webapp_dir(context)
diff --git a/orchestra/contrib/webapps/migrations/0003_auto_20230728_1639.py b/orchestra/contrib/webapps/migrations/0003_auto_20230728_1639.py
new file mode 100644
index 00000000..68e316b2
--- /dev/null
+++ b/orchestra/contrib/webapps/migrations/0003_auto_20230728_1639.py
@@ -0,0 +1,20 @@
+# Generated by Django 2.2.28 on 2023-07-28 14:39
+
+from django.conf import settings
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('orchestration', '__first__'),
+ ('webapps', '0002_webapp_sftpuser'),
+ ]
+
+ operations = [
+ migrations.AlterUniqueTogether(
+ name='webapp',
+ unique_together={('name', 'account', 'target_server')},
+ ),
+ ]
diff --git a/orchestra/contrib/webapps/models.py b/orchestra/contrib/webapps/models.py
index 2f1fe374..642922a4 100644
--- a/orchestra/contrib/webapps/models.py
+++ b/orchestra/contrib/webapps/models.py
@@ -34,7 +34,7 @@ class WebApp(models.Model):
databaseusers = VirtualDatabaseUserRelation('databases.DatabaseUser')
class Meta:
- unique_together = ('name', 'account')
+ unique_together = ('name', 'account', 'target_server')
verbose_name = _("Web App")
verbose_name_plural = _("Web Apps")
diff --git a/orchestra/contrib/webapps/settings.py b/orchestra/contrib/webapps/settings.py
index a5c5476d..1f5ae9da 100644
--- a/orchestra/contrib/webapps/settings.py
+++ b/orchestra/contrib/webapps/settings.py
@@ -33,7 +33,7 @@ WEBAPPS_FPM_DEFAULT_MAX_CHILDREN = Setting('WEBAPPS_FPM_DEFAULT_MAX_CHILDREN',
WEBAPPS_PHPFPM_POOL_PATH = Setting('WEBAPPS_PHPFPM_POOL_PATH',
- '/etc/php%(php_version_number)s/fpm/pool.d/%(user)s-%(app_name)s.conf',
+ '/etc/php/%(php_version_number)s/fpm/pool.d/%(user)s-%(app_name)s.conf',
help_text="Available fromat names: %s" % ', '.join(_php_names),
validators=[Setting.string_format_validator(_php_names)],
)
@@ -283,10 +283,3 @@ WEBAPPS_CMS_CACHE_DIR = Setting('WEBAPPS_CMS_CACHE_DIR',
help_text="Server-side cache directori for CMS tarballs.",
)
-WEBAPP_NEW_SERVERS = Setting('WEBAPP_NEW_SERVERS',
- (
- 'bookworm',
- 'web-11.pangea.lan',
- 'web-12.pangea.lan',
- )
-)
diff --git a/orchestra/contrib/webapps/types/misc.py b/orchestra/contrib/webapps/types/misc.py
index 977bac6e..92fd1fa3 100644
--- a/orchestra/contrib/webapps/types/misc.py
+++ b/orchestra/contrib/webapps/types/misc.py
@@ -2,64 +2,14 @@ import os
from django import forms
from django.utils.translation import gettext_lazy as _
-from django.core.exceptions import ValidationError
from rest_framework import serializers
-
-from orchestra.core import validators
-from orchestra.plugins.forms import PluginDataForm
-from orchestra.utils.python import random_ascii
+from orchestra.plugins.forms import ExtendedPluginDataForm
from ..options import AppOption
-from ..settings import WEBAPP_NEW_SERVERS
-
from . import AppType
from .php import PHPApp, PHPAppForm, PHPAppSerializer
-class StaticForm(PluginDataForm):
- username = forms.CharField(label=_("Username"), max_length=16,
- required=False, validators=[validators.validate_name],
- help_text=_("Required. 16 characters or fewer. Letters, digits and "
- "@/./+/-/_ only."),
- error_messages={
- 'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
- "@/./+/-/_ characters.")})
- password1 = forms.CharField(label=_("Password"), required=False,
- widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
- validators=[validators.validate_password],
- help_text=_("Suggestion: %s") % random_ascii(15))
- password2 = forms.CharField(label=_("Password confirmation"), required=False,
- widget=forms.PasswordInput,
- help_text=_("Enter the same password as above, for verification."))
-
-
- def __init__(self, *args, **kwargs):
- super(StaticForm, self).__init__(*args, **kwargs)
- self.fields['sftpuser'].widget = forms.HiddenInput()
- if self.instance.id is not None:
- self.fields['username'].widget = forms.HiddenInput()
- self.fields['password1'].widget = forms.HiddenInput()
- self.fields['password2'].widget = forms.HiddenInput()
-
- def clean(self):
- if not self.instance.id:
- webapp_server = self.cleaned_data.get("target_server")
- username = self.cleaned_data.get('username')
- if webapp_server is None:
- self.add_error("target_server", _("choice some target_server"))
- else:
- if webapp_server.name in WEBAPP_NEW_SERVERS and username == '':
- self.add_error("username", _("SFTP user is required by new webservers"))
-
- def clean_password2(self):
- password1 = self.cleaned_data.get("password1")
- password2 = self.cleaned_data.get("password2")
- if password1 and password2 and password1 != password2:
- msg = _("The two password fields didn't match.")
- raise ValidationError(msg)
- return password2
-
-
class StaticApp(AppType):
name = 'static'
verbose_name = "Static"
@@ -67,7 +17,7 @@ class StaticApp(AppType):
"Apache2 will be used to serve static content and execute CGI files.")
icon = 'orchestra/icons/apps/Static.png'
option_groups = (AppOption.FILESYSTEM,)
- form = StaticForm
+ form = ExtendedPluginDataForm
def get_directive(self):
return ('static', self.instance.get_path())
diff --git a/orchestra/contrib/webapps/types/php.py b/orchestra/contrib/webapps/types/php.py
index accdb0a8..4aff667f 100644
--- a/orchestra/contrib/webapps/types/php.py
+++ b/orchestra/contrib/webapps/types/php.py
@@ -5,9 +5,10 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
-from orchestra.plugins.forms import PluginDataForm
+from orchestra.plugins.forms import PluginDataForm, ExtendedPluginDataForm
from orchestra.utils.functional import cached
-from orchestra.utils.python import OrderedSet
+from orchestra.utils.python import OrderedSet, random_ascii
+from orchestra.settings import NEW_SERVERS
from .. import settings, utils
from ..options import AppOption
@@ -19,13 +20,23 @@ help_message = _("Version of PHP used to execute this webapp.
"
"Changing the PHP version may result in application malfunction, "
"make sure that everything continue to work as expected.")
-
-class PHPAppForm(PluginDataForm):
+class PHPAppForm(ExtendedPluginDataForm):
php_version = forms.ChoiceField(label=_("PHP version"),
choices=settings.WEBAPPS_PHP_VERSIONS,
initial=settings.WEBAPPS_DEFAULT_PHP_VERSION,
help_text=help_message)
-
+
+ # def clean_php_version(self):
+ # # TODO: restriccin PHP diferentes servers
+ # if not self.instance.id:
+ # webapp_server = self.cleaned_data.get("target_server")
+ # php_version = self.cleaned_data.get('php_version')
+ # if webapp_server is None:
+ # pass
+ # else:
+ # if webapp_server.name in NEW_SERVERS and not username:
+ # self.add_error("php_version", _(f"Server {webapp_server} not allow {php_version}"))
+
class PHPAppSerializer(serializers.Serializer):
php_version = serializers.ChoiceField(label=_("PHP version"),
diff --git a/orchestra/plugins/forms.py b/orchestra/plugins/forms.py
index d788f369..16885756 100644
--- a/orchestra/plugins/forms.py
+++ b/orchestra/plugins/forms.py
@@ -4,6 +4,12 @@ from django.utils.encoding import force_str
from orchestra.admin.utils import admin_link
from orchestra.forms.widgets import SpanWidget
+from django.core.exceptions import ValidationError
+from orchestra.core import validators
+from orchestra.utils.python import random_ascii
+from orchestra.settings import NEW_SERVERS
+from django.utils.translation import gettext_lazy as _
+
class PluginForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
@@ -71,3 +77,51 @@ class PluginModelAdapterForm(PluginForm):
display = '%s change' % link
self.fields[self.plugin_field].widget = SpanWidget(original=value, display=display)
help_text = self.fields[self.plugin_field].help_text
+
+
+# --------------------------------------------------
+
+class ExtendedPluginDataForm(PluginDataForm):
+ # aƱade campos de username para creacion de sftpuser en servidores nuevos
+ username = forms.CharField(label=_("Username"), max_length=16,
+ required=False, validators=[validators.validate_name],
+ help_text=_("Required. 16 characters or fewer. Letters, digits and "
+ "@/./+/-/_ only."),
+ error_messages={
+ 'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
+ "@/./+/-/_ characters.")})
+ password1 = forms.CharField(label=_("Password"), required=False,
+ widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
+ validators=[validators.validate_password],
+ help_text=_("Suggestion: %s") % random_ascii(15))
+ password2 = forms.CharField(label=_("Password confirmation"), required=False,
+ widget=forms.PasswordInput,
+ help_text=_("Enter the same password as above, for verification."))
+
+
+ def __init__(self, *args, **kwargs):
+ super(ExtendedPluginDataForm, self).__init__(*args, **kwargs)
+ self.fields['sftpuser'].widget = forms.HiddenInput()
+ if self.instance.id is not None:
+ self.fields['username'].widget = forms.HiddenInput()
+ self.fields['password1'].widget = forms.HiddenInput()
+ self.fields['password2'].widget = forms.HiddenInput()
+
+ def clean_username(self):
+ if not self.instance.id:
+ webapp_server = self.cleaned_data.get("target_server")
+ username = self.cleaned_data.get('username')
+ if webapp_server is None:
+ self.add_error("target_server", _("choice some server"))
+ else:
+ if webapp_server.name in NEW_SERVERS and not username:
+ self.add_error("username", _("SFTP user is required by new webservers"))
+ return username
+
+ def clean_password2(self):
+ password1 = self.cleaned_data.get("password1")
+ password2 = self.cleaned_data.get("password2")
+ if password1 and password2 and password1 != password2:
+ msg = _("The two password fields didn't match.")
+ raise ValidationError(msg)
+ return password2
\ No newline at end of file
diff --git a/orchestra/settings.py b/orchestra/settings.py
index 86c39ca8..be3a191f 100644
--- a/orchestra/settings.py
+++ b/orchestra/settings.py
@@ -90,3 +90,11 @@ ORCHESTRA_SSH_CONTROL_PATH = Setting('ORCHESTRA_SSH_CONTROL_PATH',
'~/.ssh/orchestra-%r-%h-%p',
help_text='Location for the control socket used by the multiplexed sessions, used for SSH connection reuse.'
)
+
+NEW_SERVERS = Setting('NEW_SERVERS',
+ (
+ 'bookworm',
+ 'web-11.pangea.lan',
+ 'web-12.pangea.lan',
+ )
+)