Fakultas Ilmu Komputer UI

Commit 0b1c295b authored by insan ramadhan's avatar insan ramadhan
Browse files

Merge branch 'master' into '1906457480-111'

# Conflicts:
#   app/migrations/0025_review.py
parents 3f509f4f 04c1704b
Pipeline #59962 passed with stages
in 27 minutes and 44 seconds
......@@ -16,6 +16,32 @@ pylint:
paths:
- ./pylint/
.development:
variables:
IMAGE_SCOPE_TAG: "$CI_COMMIT_BRANCH"
except:
- master
environment: development
.production:
variables:
IMAGE_SCOPE_TAG: stable
only:
- master
environment: production
.docker-image:
stage: deploy
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [ "" ]
variables:
CONTEXT: $CI_PROJECT_DIR
script:
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context ${CONTEXT} --dockerfile ${CONTEXT}/Dockerfile --destination ${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} --destination ${CI_REGISTRY_IMAGE}:${IMAGE_SCOPE_TAG}
UnitTest:
services:
- postgres:alpine
......@@ -68,3 +94,14 @@ Deployment:
environment:
name: staging
url: $HEROKU_APP_HOST_STAGING
Dev Docker Image:
extends:
- .docker-image
- .development
allow_failure: true
Prod Docker Image:
extends:
- .docker-image
- .production
\ No newline at end of file
# Generated by Django 3.1 on 2020-10-29 13:03
# Generated by Django 3.1 on 2020-10-29 11:22
import app.models
from django.conf import settings
......
......@@ -134,6 +134,10 @@ class DetailMateriService:
user_name = request.user.name
return user_name
@staticmethod
def init_materi_download_count(context, materi):
context["materi_download_count"] = materi.unduh.all().count()
class CitationService:
@staticmethod
......
......@@ -164,6 +164,14 @@ div.review {
<p class="info-content">{{materi_data.content.size|filesizeformat}}</p>
</dd>
</div>
<div class="info" id="1">
<dl class="col col-4">
<dt class="info-name">Jumlah Download</dt>
</dl>
<dd>
<p class="info-content">{{materi_download_count}}</p>
</dd>
</div>
</div>
<div class="buttons d-flex flex-row bd-highlight mb-1">
<a href="{% url 'view-materi' materi_data.id %}"
......
import json, tempfile, os, mock, base64
import pandas as pd
from io import StringIO
import re
import time
from django.test import override_settings
......@@ -42,6 +43,10 @@ from .models import (
ViewStatistics,
)
from .services import (
DetailMateriService,
)
from .views import (
DaftarKatalog,
DashboardKontributorView,
......@@ -62,6 +67,7 @@ from .views import (
)
from app.forms import SuntingProfilForm, year_choices
from app.utils.fileManagementUtil import get_random_filename, remove_image_exifdata
from app.utils.PasswordValidator import PasswordPolicyValidator
ERROR_403_MESSAGE = "Kamu harus login untuk mengakses halaman ini"
......@@ -277,6 +283,20 @@ class DaftarKatalogPerKontributorTest(TestCase):
class DetailMateriTest(TestCase):
def _get_materi_info_html(self, info_name, info_value):
info_html = '<div class="info" id="1"><dl class="col col-4">'
info_html += f'<dt class="info-name">{info_name}</dt>' + '</dl><dd>'
info_html += f'<p class="info-content">{info_value}</p>' + '</dd></div>'
return info_html
def check_materi_info_in_html(self, info_name, info_value, html_content):
expected_content = self._get_materi_info_html(info_name, info_value)
self.assertIn(expected_content, re.sub(">\s*<","><", html_content))
def check_materi_info_not_in_html(self, info_name, info_value, html_content):
expected_content = self._get_materi_info_html(info_name, info_value)
self.assertNotIn(expected_content, re.sub(">\s*<","><", html_content))
def setUp(self):
self.client = Client()
self.admin_credential = {
......@@ -302,11 +322,19 @@ class DetailMateriTest(TestCase):
"ExampleCover921.jpg", b"Test file")
self.content = SimpleUploadedFile("ExampleFile921.pdf", b"Test file")
Materi(title="Materi 1", author="Agas", uploader=self.contributor,
publisher="Kelas SC", descriptions="Deskripsi Materi 1",
status="APPROVE", cover=self.cover, content=self.content).save()
self.materi1 = Materi.objects.first()
self.materi1 = Materi.objects.create(title="Materi 1", author="Agas",
uploader=self.contributor, publisher="Kelas SC",
descriptions="Deskripsi Materi 1", status="APPROVE",
cover=self.cover, content=self.content)
self.materi2 = Materi.objects.create(title="Materi 2", author="Agad",
uploader=self.contributor, publisher="Kelas SM",
descriptions="Deskripsi Materi 2", status="APPROVE",
cover=self.cover, content=self.content)
self.url = "/materi/" + str(self.materi1.id) + "/"
self.download_url1 = self.url + "unduh"
self.url2 = "/materi/" + str(self.materi2.id) + "/"
self.download_url2 = self.url2 + "unduh"
self.dcount_info_name = "Jumlah Download"
self.materi_with_published_date = Materi.objects.create(title="Materi 1", author="Agas", uploader=self.contributor,
publisher="Kelas SC", descriptions="Deskripsi Materi 1",
......@@ -698,6 +726,95 @@ class DetailMateriTest(TestCase):
self.assertEqual(last_url, '/materi/%d/' % self.materi1.id)
self.assertEqual(status_code, 302)
def test_download_count_is_in_init_context(self):
context = {}
DetailMateriService.init_materi_download_count(context, self.materi1)
self.assertIn('materi_download_count', context.keys())
def test_download_count_is_integer(self):
context = {}
DetailMateriService.init_materi_download_count(context, self.materi1)
self.assertEqual(type(context['materi_download_count']), int)
def test_download_count_when_no_download(self):
context = {}
DetailMateriService.init_materi_download_count(context, self.materi1)
self.assertEqual(context['materi_download_count'], 0)
def test_download_count_when_single_download(self):
self.client.get(self.download_url1)
context = {}
DetailMateriService.init_materi_download_count(context, self.materi1)
self.assertEqual(context['materi_download_count'], 1)
def test_download_count_when_multiple_download(self):
self.client.get(self.download_url1)
self.client.get(self.download_url1)
self.client.get(self.download_url1)
context = {}
DetailMateriService.init_materi_download_count(context, self.materi1)
self.assertEqual(context['materi_download_count'], 3)
def test_different_material_has_different_download_count(self):
self.client.get(self.download_url1)
self.client.get(self.download_url1)
self.client.get(self.download_url1)
self.client.get(self.download_url2)
self.client.get(self.download_url2)
context1 = {}
context2 = {}
DetailMateriService.init_materi_download_count(context1, self.materi1)
DetailMateriService.init_materi_download_count(context2, self.materi2)
self.assertNotEqual(context1['materi_download_count'], context2['materi_download_count'])
self.assertEqual(context1['materi_download_count'], 3)
self.assertEqual(context2['materi_download_count'], 2)
def test_download_count_displayed_on_template_when_no_download(self):
response = self.client.get(self.url)
html = response.content.decode("utf-8")
self.check_materi_info_in_html(self.dcount_info_name, 0, html)
def test_download_count_displayed_on_template_when_single_download(self):
self.client.get(self.download_url1)
response = self.client.get(self.url)
html = response.content.decode("utf-8")
self.check_materi_info_in_html(self.dcount_info_name, 1, html)
def test_download_count_displayed_on_template_when_multiple_download(self):
self.client.get(self.download_url1)
self.client.get(self.download_url1)
self.client.get(self.download_url1)
self.client.get(self.download_url1)
response = self.client.get(self.url)
html = response.content.decode("utf-8")
self.check_materi_info_in_html(self.dcount_info_name, 4, html)
def test_different_material_has_different_download_count_on_templates(self):
self.client.get(self.download_url1)
self.client.get(self.download_url1)
self.client.get(self.download_url2)
self.client.get(self.download_url2)
self.client.get(self.download_url2)
self.client.get(self.download_url2)
response = self.client.get(self.url)
response2 = self.client.get(self.url2)
html = response.content.decode("utf-8")
html2 = response2.content.decode("utf-8")
dcount_materi1 = self.materi1.unduh.all().count()
dcount_materi2 = self.materi2.unduh.all().count()
self.check_materi_info_in_html(self.dcount_info_name, dcount_materi1, html)
self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi2, html)
self.check_materi_info_in_html(self.dcount_info_name, dcount_materi2, html2)
self.check_materi_info_not_in_html(self.dcount_info_name, dcount_materi1, html2)
class PostsViewTest(TestCase):
......@@ -2886,3 +3003,32 @@ class SeeRatedMateriByUser(TestCase):
self.assertEqual(list(response.context['rating_list']),
[self.rating_test_2, self.rating_test_1, self.rating_test_3])
class PasswordValidatorPolicyTest(TestCase):
def setUp(self):
self.password_no_lowercase = "PASSW0RD!"
self.password_no_uppercase = "passw0rd!"
self.password_no_digit = "Password!"
self.password_no_special_char = "Passw0rd"
self.password_length_lower_than_8 = "P4ss!"
self.password_enforcing_policy = "Passw0rd!"
self.validator = PasswordPolicyValidator()
def test_using_password_no_lowercase(self):
self.assertRaises(ValidationError, self.validator.validate, self.password_no_lowercase)
def test_using_password_no_upprcase(self):
self.assertRaises(ValidationError, self.validator.validate, self.password_no_uppercase)
def test_using_password_no_digit(self):
self.assertRaises(ValidationError, self.validator.validate, self.password_no_digit)
def test_using_password_no_special_char(self):
self.assertRaises(ValidationError, self.validator.validate, self.password_no_special_char)
def test_using_password_with_length_less_than_8(self):
self.assertRaises(ValidationError, self.validator.validate, self.password_length_lower_than_8)
def test_using_password_using_correct_policy(self):
self.assertEquals(self.validator.validate(self.password_enforcing_policy), None)
\ No newline at end of file
import string
from django.core.exceptions import ValidationError
class PasswordPolicyValidator(object):
def validate(self, password, user=None):
if sum(c.isdigit() for c in password) < 1:
msg = 'Password must contain at least 1 number.'
raise ValidationError(msg)
if not any(c.isupper() for c in password):
msg = 'Password must contain at least 1 uppercase letter.'
raise ValidationError(msg)
if not any(c.islower() for c in password):
msg = 'Password must contain at least 1 lowercase letter.'
raise ValidationError(msg)
if not any(c for c in password if c in string.punctuation):
msg = 'Password must contain at least 1 special letter.'
raise ValidationError(msg)
if len(password) < 8 :
msg = 'Password must have at least 8 characters.'
raise ValidationError(msg)
def get_help_text(self):
return (
"Password must contains at least 8 character with combination of lower case letter, upper case letter, digit, and symbol."
)
......@@ -113,6 +113,7 @@ class DetailMateri(TemplateView):
DetailMateriService.init_context_data(context, materi, self.request.session)
published_date = DetailMateriService.set_published_date(materi)
DetailMateriService.init_citation_and_materi_rating(context, materi, published_date, self.request)
DetailMateriService.init_materi_download_count(context, materi)
if self.request.user.is_authenticated:
materi_rating = Rating.objects.filter(materi=materi, user=self.request.user).first()
......
......@@ -127,10 +127,7 @@ STATICFILES_DIRS = (os.path.join(BASE_DIR, "staticfiles"),)
AUTH_USER_MODEL = "authentication.User"
AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", },
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", },
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", },
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", },
{"NAME": "app.utils.PasswordValidator.PasswordPolicyValidator", },
]
......
from django.contrib.auth.hashers import make_password
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
class RegistrationService:
@staticmethod
def create_new_contributor(data, form):
create_result = dict()
create_result["success"] = True
new_user = form.save(commit=False)
new_user.password = make_password(data["password"])
new_user.is_contributor = True
new_user.save()
return new_user
try:
password = form.cleaned_data['password']
validate_password(password, new_user)
new_user.password = make_password(data["password"])
new_user.is_contributor = True
new_user.save()
create_result["user"] = new_user
except ValidationError as e:
create_result["success"] = False
form.add_error('password', e)
create_result["form"] = form
return create_result
@staticmethod
def create_new_admin(data, form):
create_result = dict()
create_result["success"] = True
new_user = form.save(commit=False)
new_user.password = make_password(data["password"])
new_user.is_admin = True
new_user.is_active = False
new_user.save()
\ No newline at end of file
try:
password = form.cleaned_data['password']
validate_password(password, new_user)
new_user.password = make_password(data["password"])
new_user.is_admin = True
new_user.is_active = False
new_user.save()
create_result["user"] = new_user
except ValidationError as e:
create_result["success"] = False
form.add_error('password', e)
create_result["form"] = form
return create_result
\ No newline at end of file
......@@ -10,6 +10,7 @@ from register import views
class RegisterPageTest(TestCase):
def setUp(self):
self.client = Client()
self.password = "Passw0rd!"
def test_register_url_is_exist(self):
# Positive tests
......@@ -64,8 +65,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "1234",
"password2": "1234",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -80,8 +81,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "1234",
"password2": "12345",
"password": self.password,
"password2": "different passwd",
},
)
self.assertEqual(User.objects.all().count(), 0)
......@@ -97,8 +98,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "1234",
"password2": "1234",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -111,8 +112,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "1234",
"password2": "1234",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -129,8 +130,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726601",
"password": "123456",
"password2": "123456",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -144,8 +145,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "budi@company.com",
"nomor_telpon": "087878726602",
"password": "123456",
"password2": "123456",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -162,8 +163,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "123456",
"password2": "123456",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -177,8 +178,8 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "budi@company.com",
"nomor_telpon": "087878726602",
"password": "123456",
"password2": "123456",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -195,17 +196,102 @@ class RegisterPageTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "abcdefghijkl",
"password": "1234",
"password2": "12345",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 0)
self.assertIn(b"Hanya masukkan angka", response.content)
def test_create_user_weak_password_no_lowercase(self):
response = self.client.post(
"/registrasi/",
{
"name": "bob",
"instansi": "university",
"nik": "3201234567890001",
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "PASSW0RD!",
"password2": "PASSW0RD!",
},
)
self.assertEqual(User.objects.all().count(), 0)
self.assertIn(b"Password must contain at least 1 lowercase letter", response.content)
def test_create_user_weak_password_no_uppercase(self):
response = self.client.post(
"/registrasi/",
{
"name": "bob",
"instansi": "university",
"nik": "3201234567890001",
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "passw0rd!",
"password2": "passw0rd!",
},
)
self.assertEqual(User.objects.all().count(), 0)
self.assertIn(b"Password must contain at least 1 uppercase letter", response.content)
def test_create_user_weak_password_no_special_char(self):
response = self.client.post(
"/registrasi/",
{
"name": "bob",
"instansi": "university",
"nik": "3201234567890001",
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "Passw0rd",
"password2": "Passw0rd",
},
)
self.assertEqual(User.objects.all().count(), 0)
self.assertIn(b"Password must contain at least 1 special letter", response.content)
def test_create_user_weak_password_no_number(self):
response = self.client.post(
"/registrasi/",
{
"name": "bob",
"instansi": "university",
"nik": "3201234567890001",
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "Password!",
"password2": "Password!",
},
)
self.assertEqual(User.objects.all().count(), 0)
self.assertIn(b"Password must contain at least 1 number", response.content)
def test_create_user_weak_password_less_than_8_chars(self):
response = self.client.post(
"/registrasi/",
{
"name": "bob",
"instansi": "university",
"nik": "3201234567890001",
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "P4ss!",
"password2": "P4ss!",
},
)
self.assertEqual(User.objects.all().count(), 0)
self.assertIn(b"Password must have at least 8 characters", response.content)
class RegisterAdminTest(TestCase):
def setUp(self):
self.client = Client()
self.random_password = id_generator()
self.password = "Passw0rd!"
def test_register_url_is_exist(self):
# Positive tests
......@@ -260,8 +346,8 @@ class RegisterAdminTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "1234",
"password2": "1234",
"password": self.password,
"password2": self.password,
},
)
self.assertEqual(User.objects.all().count(), 1)
......@@ -276,8 +362,8 @@ class RegisterAdminTest(TestCase):
"alamat": "bekasi",
"email": "bob@company.com",
"nomor_telpon": "087878726602",
"password": "1234",
"password2": "12345",
"password": self.password,
"password2": "different passwd",
},