init
Some checks failed
Build And Test / build-and-push (push) Failing after 53s

This commit is contained in:
2024-12-13 17:12:03 +09:00
parent a27be94b19
commit ea11832a53
695 changed files with 71766 additions and 1 deletions

0
custom_auth/__init__.py Normal file
View File

27
custom_auth/admin.py Normal file
View File

@ -0,0 +1,27 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
# admin.site.register(CustomUser)
class CustomUserAdmin(UserAdmin):
"""사용자 관리자 페이지 커스터마이징"""
model = CustomUser
list_display = ('username', 'email', 'is_staff', 'is_active')
list_filter = ('is_staff', 'is_active')
# 사용자 필드 구성
fieldsets = (
(None, {'fields': ('username', 'password')}),
('Personal Info', {'fields': ('email', 'encrypted_private_key')}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
)
# 읽기 전용 필드 추가
readonly_fields = ('encrypted_private_key',)
search_fields = ('username', 'email')
ordering = ('username',)
# 관리자 등록
admin.site.register(CustomUser, CustomUserAdmin)

6
custom_auth/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class CustomAuthConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'custom_auth'

67
custom_auth/forms.py Normal file
View File

@ -0,0 +1,67 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ("username", "email", "grade") # 필요에 따라 추가 필드 지정
class CustomUserChangeForm(UserChangeForm):
password = forms.CharField(
label="새 비밀번호",
required=False,
widget=forms.PasswordInput(attrs={"class": "form-control"}),
)
password_confirm = forms.CharField(
label="비밀번호 확인",
required=False,
widget=forms.PasswordInput(attrs={"class": "form-control"}),
)
class Meta:
model = CustomUser
fields = ["email", "nhnc_id", "nhnc_api_tenant_id"]
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
password_confirm = cleaned_data.get("password_confirm")
if password and password != password_confirm:
raise forms.ValidationError("비밀번호가 일치하지 않습니다.")
return cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
password = self.cleaned_data.get("password")
if password:
user.set_password(password)
if commit:
user.save()
return user
class SSHKeyForm(forms.ModelForm):
"""SSH Private Key 관리 폼"""
private_key = forms.CharField(
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 5,
'placeholder': 'Enter your SSH Private Key',
}),
required=True,
label="SSH Private Key"
)
class Meta:
model = CustomUser
fields = [] # 모델 필드는 직접 관리하므로 비움
def save(self, commit=True):
"""SSH Private Key 암호화 후 저장"""
user = self.instance
private_key = self.cleaned_data['private_key']
user.save_private_key(private_key)
return user

View File

@ -0,0 +1,45 @@
# Generated by Django 4.2.14 on 2024-11-05 02:06
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='CustomUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('grade', models.CharField(blank=True, max_length=50, null=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.14 on 2024-11-12 03:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('custom_auth', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='customuser',
name='tenant_id_kr1',
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name='customuser',
name='tenant_id_kr2',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.14 on 2024-11-12 13:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('custom_auth', '0002_customuser_tenant_id_kr1_customuser_tenant_id_kr2'),
]
operations = [
migrations.AddField(
model_name='customuser',
name='nhnc_id',
field=models.CharField(blank=True, max_length=100, null=True),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.14 on 2024-11-12 13:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('custom_auth', '0003_customuser_nhnc_id'),
]
operations = [
migrations.RenameField(
model_name='customuser',
old_name='tenant_id_kr1',
new_name='nhnc_api_tenant_id',
),
migrations.RemoveField(
model_name='customuser',
name='tenant_id_kr2',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.14 on 2024-12-12 13:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('custom_auth', '0004_rename_tenant_id_kr1_customuser_nhnc_api_tenant_id_and_more'),
]
operations = [
migrations.AddField(
model_name='customuser',
name='encrypted_private_key',
field=models.BinaryField(blank=True, null=True),
),
]

View File

42
custom_auth/models.py Normal file
View File

@ -0,0 +1,42 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
import base64
from cryptography.fernet import Fernet
class CustomUser(AbstractUser):
"""사용자 모델 - 기존 필드 + SSH Private Key 관리 필드"""
# 기존 필드 유지
grade = models.CharField(max_length=50, blank=True, null=True)
nhnc_id = models.CharField(max_length=100, blank=True, null=True)
nhnc_api_tenant_id = models.CharField(max_length=100, blank=True, null=True)
"""사용자 모델 - SSH Private Key 관리 필드"""
encrypted_private_key = models.BinaryField(blank=True, null=True) # 암호화된 SSH 키
def encrypt_private_key(self, private_key: str) -> bytes:
"""SSH Private Key 암호화"""
cipher = Fernet(self.get_encryption_key())
return cipher.encrypt(private_key.encode())
def decrypt_private_key(self) -> str:
"""SSH Private Key 복호화"""
if self.encrypted_private_key:
cipher = Fernet(self.get_encryption_key())
return cipher.decrypt(self.encrypted_private_key).decode()
return ""
def save_private_key(self, private_key: str):
"""SSH Private Key 저장 (암호화 후)"""
self.encrypted_private_key = self.encrypt_private_key(private_key)
self.save()
def get_encryption_key(self) -> bytes:
"""Fernet 키 생성 (username 기반)"""
username_encoded = self.username.encode() # 사용자 이름을 바이트로 인코딩
base64_key = base64.urlsafe_b64encode(username_encoded.ljust(32)[:32])
return base64_key
def __str__(self):
return self.username

View File

@ -0,0 +1,27 @@
{% extends "components/base.html" %}
{% block content %}
<h2>Login</h2>
<!-- 로그인 실패 메시지 표시 -->
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required="required">
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required="required">
</div>
<button type="submit" class="btn btn-primary w-100">Login</button>
</form>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends "components/base.html" %}
{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends "components/base.html" %}
{% block content %}
<h2>Welcome, {{ user.username }}!</h2>
<p>This is your profile page.</p>
{% endblock %}

View File

@ -0,0 +1,10 @@
{% extends "components/base.html" %}
{% block content %}
<h2>Sign Up</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Sign Up</button>
</form>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "components/base.html" %}
{% block main_area %}
<h1>Upload SSH Private Key</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Upload</button>
</form>
<!-- 복호화된 SSH Key 표시 -->
{% if decrypted_key %}
<hr>
<p class="text-muted mt-4">Upload 클릭 후 이전 SSH Key가 업데이트 됩니다.</p>
<h3>Stored SSH Private Key (Decrypted)</h3>
<pre class="bg-light p-3">{{ decrypted_key }}</pre>
{% else %}
<!-- <p class="text-muted mt-4">No SSH Private Key stored.</p> -->
<p class="text-muted mt-4">Upload 클릭 후 이전 SSH Key가 업데이트 됩니다.</p>
{% endif %}
{% endblock %}

3
custom_auth/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

14
custom_auth/urls.py Normal file
View File

@ -0,0 +1,14 @@
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
app_name = 'custom_auth'
urlpatterns = [
# path('login/', auth_views.LoginView.as_view(template_name='custom_auth/login.html'), name='login'),
path('login/', views.login_view, name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('signup/', views.signup_view, name='signup'),
path('edit-profile/', views.edit_profile_view, name='edit_profile'),
path('upload_ssh_key/', views.upload_ssh_key, name='upload_ssh_key'),
]

94
custom_auth/views.py Normal file
View File

@ -0,0 +1,94 @@
from django.shortcuts import render, redirect
from .forms import CustomUserCreationForm, CustomUserChangeForm
from django.contrib.auth import login, authenticate
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth import update_session_auth_hash
from django.shortcuts import render, redirect
from .forms import SSHKeyForm
@login_required
def edit_profile_view(request):
if request.method == "POST":
form = CustomUserChangeForm(request.POST, instance=request.user)
if form.is_valid():
user = form.save()
# 세션 인증 유지
update_session_auth_hash(request, user)
messages.success(request, "프로필이 성공적으로 업데이트되었습니다.")
return redirect("/") # 리다이렉트할 URL
else:
form = CustomUserChangeForm(instance=request.user)
return render(request, "custom_auth/edit_profile.html", {"form": form})
def signup_view(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
# 로그인 처리 (선택적)
# login(request, user)
return redirect("/")
# return redirect('custom_auth:login')
else:
form = CustomUserCreationForm()
return render(request, "custom_auth/signup.html", {"form": form})
def login_view(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect("/") # 원하는 URL로 리다이렉트 (여기서는 'home'으로 설정)
else:
messages.error(request, "Invalid username or password.")
return redirect("/") # 로그인 실패 시 루트 URL로 리다이렉트
return render(request, "custom_auth/login.html")
@login_required
# def upload_ssh_key(request):
# """SSH Private Key 업로드 뷰"""
# user = request.user
# if request.method == 'POST':
# form = SSHKeyForm(request.POST, instance=user)
# if form.is_valid():
# form.save()
# return redirect('custom_auth:upload_ssh_key') # 성공 시 동일 페이지 리다이렉트
# else:
# form = SSHKeyForm(instance=user)
# return render(request, 'custom_auth/upload_ssh_key.html', {'form': form})
# @login_required
def upload_ssh_key(request):
"""SSH Private Key 업로드 및 복호화 표시 뷰"""
user = request.user
decrypted_key = "" # 기본 값
# POST 요청 처리
if request.method == 'POST':
form = SSHKeyForm(request.POST, instance=user)
if form.is_valid():
form.save()
return redirect('custom_auth:upload_ssh_key') # 성공 시 리다이렉트
else:
form = SSHKeyForm(instance=user)
# 복호화된 SSH Key 가져오기
if user.encrypted_private_key:
try:
decrypted_key = user.decrypt_private_key()
except Exception as e:
decrypted_key = f"복호화 오류: {str(e)}"
# 템플릿으로 데이터 전달
return render(request, 'custom_auth/upload_ssh_key.html', {
'form': form,
'decrypted_key': decrypted_key
})