Browse Source

Add logging to admin actions

devel
Dmitry Novikov 8 years ago
parent
commit
535389b9c4
  1. 129
      abonapp/locale/ru/LC_MESSAGES/django.po
  2. 11
      abonapp/views.py
  3. 3
      accounts_app/admin.py
  4. 142
      accounts_app/locale/ru/LC_MESSAGES/django.po
  5. 45
      accounts_app/migrations/0003_new_user_profile_log.py
  6. 56
      accounts_app/models.py
  7. 29
      accounts_app/templates/accounts/action_log.html
  8. 11
      accounts_app/templates/accounts/ext.htm
  9. 4
      accounts_app/urls.py
  10. 32
      accounts_app/views.py
  11. 1
      devapp/forms.py
  12. 12
      devapp/views.py
  13. 2
      msg_app/templates/msg_app/chat.html
  14. 10
      nas_app/views.py
  15. 15
      tariff_app/views.py

129
abonapp/locale/ru/LC_MESSAGES/django.po

@ -7,7 +7,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-25 14:55+0300\n"
"POT-Creation-Date: 2018-08-26 19:21+0300\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" "Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n" "Language: ru\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -211,11 +211,12 @@ msgid "Buy service default log"
msgstr "Покупка тарифного плана через админку" msgstr "Покупка тарифного плана через админку"
#: models.py:228 #: models.py:228
#, python-format
msgid "Account \"%(username)s\" not have any active leases" msgid "Account \"%(username)s\" not have any active leases"
msgstr "Учётная запись %(username)s не имеет ни одной активной сессии"
msgstr "Учётная запись \"%(username)s\" не имеет ни одной активной сессии"
#: models.py:238 models.py:255 models.py:272 views.py:676 views.py:1124
#: views.py:1167
#: models.py:238 models.py:255 models.py:272 views.py:684 views.py:1132
#: views.py:1175
msgid "NAS required" msgid "NAS required"
msgstr "Необходимо выбрать NAS" msgstr "Необходимо выбрать NAS"
@ -352,7 +353,9 @@ msgstr "Статус оплаты"
#: templates/abonapp/addInvoice.html:57 #: templates/abonapp/addInvoice.html:57
msgid "This credit will be visible in user page, be careful with your text." msgid "This credit will be visible in user page, be careful with your text."
msgstr "Этот долг будет виден на странице абонента, будьте осторожны с высказываниями."
msgstr ""
"Этот долг будет виден на странице абонента, будьте осторожны с "
"высказываниями."
#: templates/abonapp/buy_tariff.html:10 templates/abonapp/buy_tariff.html:15 #: templates/abonapp/buy_tariff.html:10 templates/abonapp/buy_tariff.html:15
#: templates/abonapp/buy_tariff.html:31 templates/abonapp/service.html:74 #: templates/abonapp/buy_tariff.html:31 templates/abonapp/service.html:74
@ -908,134 +911,134 @@ msgstr "Это не правильный IPv6 адрес"
msgid "Address" msgid "Address"
msgstr "Адрес" msgstr "Адрес"
#: views.py:132
#: views.py:136
msgid "create abon success msg" msgid "create abon success msg"
msgstr "Абонент успешно создан" msgstr "Абонент успешно создан"
#: views.py:143 views.py:317 views.py:436 views.py:532 views.py:857
#: views.py:941 views.py:1014 views.py:1110
#: views.py:147 views.py:325 views.py:444 views.py:540 views.py:865
#: views.py:949 views.py:1022 views.py:1118
msgid "fix form errors" msgid "fix form errors"
msgstr "Некоторые поля заполнены не правильно, проверте ещё раз" msgstr "Некоторые поля заполнены не правильно, проверте ещё раз"
#: views.py:167
#: views.py:175
msgid "delete abon success msg" msgid "delete abon success msg"
msgstr "Абонент успешно удалён" msgstr "Абонент успешно удалён"
#: views.py:172
#: views.py:180
#, python-format #, python-format
msgid "NAS says: '%s'" msgid "NAS says: '%s'"
msgstr "NAS сказал: '%s'" msgstr "NAS сказал: '%s'"
#: views.py:193
#: views.py:201
msgid "fill account through admin side" msgid "fill account through admin side"
msgstr "Пополнение счёта через админку" msgstr "Пополнение счёта через админку"
#: views.py:196
#: views.py:204
#, python-format #, python-format
msgid "Account filled successfully on %.2f" msgid "Account filled successfully on %.2f"
msgstr "Счёт пополнен на %.2f" msgstr "Счёт пополнен на %.2f"
#: views.py:199
#: views.py:207
msgid "I not know the account id" msgid "I not know the account id"
msgstr "Счёт успешно пополнен на %.2f" msgstr "Счёт успешно пополнен на %.2f"
#: views.py:260
#: views.py:268
msgid "User group id is not matches with group in url" msgid "User group id is not matches with group in url"
msgstr "Группа абонента не совпадает с группой указанной в url" msgstr "Группа абонента не совпадает с группой указанной в url"
#: views.py:313
#: views.py:321
msgid "edit abon success msg" msgid "edit abon success msg"
msgstr "Абонент успешно изменён" msgstr "Абонент успешно изменён"
#: views.py:324
#: views.py:332
msgid "User device was not found" msgid "User device was not found"
msgstr "Пользовательское устройство не найдено" msgstr "Пользовательское устройство не найдено"
#: views.py:337
#: views.py:345
msgid "User has not have password, and cannot login" msgid "User has not have password, and cannot login"
msgstr "Для абонента не задан пароль, он не сможет войти в учётку" msgstr "Для абонента не задан пароль, он не сможет войти в учётку"
#: views.py:384
#: views.py:392
msgid "Receipt has been created" msgid "Receipt has been created"
msgstr "Квитанция на оплату была создана" msgstr "Квитанция на оплату была создана"
#: views.py:411
#: views.py:419
#, python-format #, python-format
msgid "Service '%(service_name)s' has connected via admin" msgid "Service '%(service_name)s' has connected via admin"
msgstr "Услуга '%(service_name)s' подключена администратором" msgstr "Услуга '%(service_name)s' подключена администратором"
#: views.py:421
#: views.py:429
msgid "Tariff has been picked" msgid "Tariff has been picked"
msgstr "Тариф успешно выбран" msgstr "Тариф успешно выбран"
#: views.py:431
#: views.py:439
msgid "Tariff your picked does not exist" msgid "Tariff your picked does not exist"
msgstr "Тариф, который вы выбрали, не существует" msgstr "Тариф, который вы выбрали, не существует"
#: views.py:453
#: views.py:461
msgid "User has been detached from service" msgid "User has been detached from service"
msgstr "Абонент отвязан от услуги" msgstr "Абонент отвязан от услуги"
#: views.py:523
#: views.py:531
msgid "Passport information has been saved" msgid "Passport information has been saved"
msgstr "Информация о паспорте сохранена" msgstr "Информация о паспорте сохранена"
#: views.py:554
#: views.py:562
msgid "Successfully saved" msgid "Successfully saved"
msgstr "Успешно сохранено" msgstr "Успешно сохранено"
#: views.py:575
#: views.py:583
msgid "Device has successfully attached" msgid "Device has successfully attached"
msgstr "Устройство успешно прикреплено" msgstr "Устройство успешно прикреплено"
#: views.py:580
#: views.py:588
msgid "Device your selected already does not exist" msgid "Device your selected already does not exist"
msgstr "Устройство, выбранное вами, уже не существует" msgstr "Устройство, выбранное вами, уже не существует"
#: views.py:582 views.py:604 views.py:642
#: views.py:590 views.py:612 views.py:650
msgid "Abon does not exist" msgid "Abon does not exist"
msgstr "Абонент не найден" msgstr "Абонент не найден"
#: views.py:602
#: views.py:610
msgid "Device has successfully unattached" msgid "Device has successfully unattached"
msgstr "Устройство успешно откреплено" msgstr "Устройство успешно откреплено"
#: views.py:645
#: views.py:653
msgid "Group what you want doesn't exist" msgid "Group what you want doesn't exist"
msgstr "Указанная вами группа не найдена" msgstr "Указанная вами группа не найдена"
#: views.py:667
#: views.py:675
msgid "no ping" msgid "no ping"
msgstr "не пингуется" msgstr "не пингуется"
#: views.py:671
#: views.py:679
msgid "Ip not passed" msgid "Ip not passed"
msgstr "Ip адрес не передан" msgstr "Ip адрес не передан"
#: views.py:683 views.py:699
#: views.py:691 views.py:707
msgid "ping ok" msgid "ping ok"
msgstr "пингуется" msgstr "пингуется"
#: views.py:690
#: views.py:698
#, python-format #, python-format
msgid "IP Conflict! %(all)d/%(return)d results" msgid "IP Conflict! %(all)d/%(return)d results"
msgstr "IP Конфликт! ping %(all)d из %(return)d" msgstr "IP Конфликт! ping %(all)d из %(return)d"
#: views.py:693
#: views.py:701
#, python-format #, python-format
msgid "ok ping, %(all)d/%(return)d loses" msgid "ok ping, %(all)d/%(return)d loses"
msgstr "пингуется, %(all)d/%(return)d" msgstr "пингуется, %(all)d/%(return)d"
#: views.py:697
#: views.py:705
#, python-format #, python-format
msgid "no ping, %(all)d/%(return)d loses" msgid "no ping, %(all)d/%(return)d loses"
msgstr "не пингуется, %(all)d/%(return)d" msgstr "не пингуется, %(all)d/%(return)d"
#: views.py:803
#: views.py:811
msgid "Method is not POST" msgid "Method is not POST"
msgstr "Метод не POST" msgstr "Метод не POST"
#: views.py:820
#: views.py:828
#, python-format #, python-format
msgid "" msgid ""
"<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this " "<a href='%(user_url)s'>%(user_name)s</a> already pinned to this port on this "
@ -1044,108 +1047,108 @@ msgstr ""
"<a href='%(user_url)s'>%(user_name)s</a> уже привязан к этому порту на этом " "<a href='%(user_url)s'>%(user_name)s</a> уже привязан к этому порту на этом "
"устройстве" "устройстве"
#: views.py:828
#: views.py:836
msgid "Multiple users on the same device port" msgid "Multiple users on the same device port"
msgstr "Несколько абонентов на одном и том же порту устройства" msgstr "Несколько абонентов на одном и том же порту устройства"
#: views.py:837
#: views.py:845
msgid "User port has been saved" msgid "User port has been saved"
msgstr "Порт абонента успешно выбран" msgstr "Порт абонента успешно выбран"
#: views.py:839
#: views.py:847
msgid "Selected port does not exist" msgid "Selected port does not exist"
msgstr "Выбранный порт не существует" msgstr "Выбранный порт не существует"
#: views.py:841
#: views.py:849
msgid "User does not exist" msgid "User does not exist"
msgstr "Абонент не найден" msgstr "Абонент не найден"
#: views.py:854
#: views.py:862
msgid "Street successfully saved" msgid "Street successfully saved"
msgstr "Улица успешно сохранена" msgstr "Улица успешно сохранена"
#: views.py:877
#: views.py:885
msgid "Streets has been saved" msgid "Streets has been saved"
msgstr "Улицы сохранены" msgstr "Улицы сохранены"
#: views.py:885
#: views.py:893
msgid "One of these streets has not been found" msgid "One of these streets has not been found"
msgstr "Одна из этих улиц не была найдена" msgstr "Одна из этих улиц не была найдена"
#: views.py:897
#: views.py:905
msgid "The street successfully deleted" msgid "The street successfully deleted"
msgstr "Улица успешно удалена" msgstr "Улица успешно удалена"
#: views.py:899
#: views.py:907
msgid "The street has not been found" msgid "The street has not been found"
msgstr "Улица не найдена" msgstr "Улица не найдена"
#: views.py:938
#: views.py:946
msgid "New telephone has been saved" msgid "New telephone has been saved"
msgstr "Новый телефон сохранен" msgstr "Новый телефон сохранен"
#: views.py:959
#: views.py:967
msgid "Additional telephone successfully deleted" msgid "Additional telephone successfully deleted"
msgstr "Номер телефона успешно удалён" msgstr "Номер телефона успешно удалён"
#: views.py:961
#: views.py:969
msgid "Telephone not found" msgid "Telephone not found"
msgstr "Телефон не найден" msgstr "Телефон не найден"
#: views.py:1011
#: views.py:1019
#, python-format #, python-format
msgid "Unexpected format %(export_format)s" msgid "Unexpected format %(export_format)s"
msgstr "Нежиданный формат %(export_format)s" msgstr "Нежиданный формат %(export_format)s"
#: views.py:1061
#: views.py:1069
msgid "Periodic pays has been designated" msgid "Periodic pays has been designated"
msgstr "Периодический платёж назначен" msgstr "Периодический платёж назначен"
#: views.py:1063
#: views.py:1071
msgid "Something wrong in form" msgid "Something wrong in form"
msgstr "Что-то не так в форме" msgstr "Что-то не так в форме"
#: views.py:1083
#: views.py:1091
msgid "Periodic pay successfully deleted" msgid "Periodic pay successfully deleted"
msgstr "Периодический платёж успешно удалён" msgstr "Периодический платёж успешно удалён"
#: views.py:1115
#: views.py:1123
msgid "User flags has changed successfully" msgid "User flags has changed successfully"
msgstr "Флаги абонента изменены успешно" msgstr "Флаги абонента изменены успешно"
#: views.py:1133
#: views.py:1141
msgid "Ip lease has been freed" msgid "Ip lease has been freed"
msgstr "Аренда ip освобождена" msgstr "Аренда ip освобождена"
#: views.py:1136
#: views.py:1144
msgid "You cannot disable last session" msgid "You cannot disable last session"
msgstr "Вы не можете отключить последний ip" msgstr "Вы не можете отключить последний ip"
#: views.py:1141
#: views.py:1149
msgid "Ip lease has been started" msgid "Ip lease has been started"
msgstr "Аренда ip включена" msgstr "Аренда ip включена"
#: views.py:1143
#: views.py:1151
msgid "Unexpected action" msgid "Unexpected action"
msgstr "Непредвиденное действие" msgstr "Непредвиденное действие"
#: views.py:1174
#: views.py:1182
msgid "Ip lease has been created" msgid "Ip lease has been created"
msgstr "Аренда ip создана" msgstr "Аренда ip создана"
#: views.py:1179
#: views.py:1187
msgid "Check form errors" msgid "Check form errors"
msgstr "Некоторые поля заполнены не правильно, проверте ещё раз" msgstr "Некоторые поля заполнены не правильно, проверте ещё раз"
#: views.py:1205
#: views.py:1213
msgid "Network access server for users in this group, has been updated" msgid "Network access server for users in this group, has been updated"
msgstr "Сервер доступа в интернет привязан к пользователям в этой группе" msgstr "Сервер доступа в интернет привязан к пользователям в этой группе"
#: views.py:1208
#: views.py:1216
msgid "Users not found" msgid "Users not found"
msgstr "Пользователи не найдены" msgstr "Пользователи не найдены"
#: views.py:1210
#: views.py:1218
msgid "You must select gateway" msgid "You must select gateway"
msgstr "Вы должны выбрать шлюз" msgstr "Вы должны выбрать шлюз"

11
abonapp/views.py

@ -129,6 +129,10 @@ class AbonCreateView(CreateView):
assign_perm("abonapp.can_buy_tariff", me, abon) assign_perm("abonapp.can_buy_tariff", me, abon)
assign_perm("abonapp.can_view_passport", me, abon) assign_perm("abonapp.can_view_passport", me, abon)
assign_perm('abonapp.can_add_ballance', me, abon) assign_perm('abonapp.can_add_ballance', me, abon)
me.log(self.request.META, 'cusr', '%s, "%s", %s' % (
abon.username, abon.fio,
abon.group.title if abon.group else ''
))
messages.success(self.request, _('create abon success msg')) messages.success(self.request, _('create abon success msg'))
self.abon = abon self.abon = abon
return super(AbonCreateView, self).form_valid(form) return super(AbonCreateView, self).form_valid(form)
@ -164,6 +168,13 @@ class DelAbonDeleteView(DeleteView):
abon = self.get_object() abon = self.get_object()
gid = abon.group.id gid = abon.group.id
abon.delete() abon.delete()
request.user.log(request.META, 'dusr', ('%(uname)s, "%(fio)s", %(group)s %(street)s %(house)s' % {
'uname': abon.username,
'fio': abon.fio or '-',
'group': abon.group.title if abon.group else '',
'street': abon.street.name if abon.street else '',
'house': abon.house or ''
}).strip())
messages.success(request, _('delete abon success msg')) messages.success(request, _('delete abon success msg'))
return redirect('abonapp:people_list', gid=gid) return redirect('abonapp:people_list', gid=gid)
except NasNetworkError as e: except NasNetworkError as e:

3
accounts_app/admin.py

@ -1,5 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import UserProfile
from .models import UserProfile, UserProfileLog
admin.site.register(UserProfile) admin.site.register(UserProfile)
admin.site.register(UserProfileLog)

142
accounts_app/locale/ru/LC_MESSAGES/django.po

@ -7,7 +7,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-09 14:54+0300\n"
"POT-Creation-Date: 2018-08-26 19:09+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Dmitry Novikov nerosketch@gmail.com\n" "Last-Translator: Dmitry Novikov nerosketch@gmail.com\n"
"Language: ru\n" "Language: ru\n"
@ -18,49 +18,105 @@ msgstr ""
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
"%100>=11 && n%100<=14)? 2 : 3);\n" "%100>=11 && n%100<=14)? 2 : 3);\n"
#: models.py:19
#: models.py:21
msgid "Users must have an telephone number" msgid "Users must have an telephone number"
msgstr "У пользователей должен быть номер телефона" msgstr "У пользователей должен быть номер телефона"
#: models.py:47 templates/accounts/acc_list.html:21
#: models.py:49 templates/accounts/acc_list.html:21
msgid "profile username" msgid "profile username"
msgstr "Логин" msgstr "Логин"
#: models.py:52
#: models.py:54
msgid "fio" msgid "fio"
msgstr "ФИО" msgstr "ФИО"
#: models.py:53
#: models.py:55
msgid "birth day" msgid "birth day"
msgstr "дата рождения" msgstr "дата рождения"
#: models.py:54
#: models.py:56
msgid "Is active" msgid "Is active"
msgstr "Активен" msgstr "Активен"
#: models.py:58 templates/accounts/acc_list.html:23
#: models.py:60 templates/accounts/acc_list.html:23
#: templates/accounts/create_acc.html:62 templates/accounts/index.html:9 #: templates/accounts/create_acc.html:62 templates/accounts/index.html:9
#: templates/accounts/settings/ch_info.html:38 #: templates/accounts/settings/ch_info.html:38
msgid "Telephone" msgid "Telephone"
msgstr "Телефон" msgstr "Телефон"
#: models.py:94
msgid "Author"
msgstr "Автор"
#: models.py:95 templates/accounts/action_log.html:12
msgid "Meta information"
msgstr "Мета информация"
#: models.py:97 #: models.py:97
msgid "Create user"
msgstr "Создание абонента"
#: models.py:98
msgid "Delete user"
msgstr "Удаление абонента"
#: models.py:99
msgid "Create device"
msgstr "Создание устройства"
#: models.py:100
msgid "Delete device"
msgstr "Удаление устройства"
#: models.py:101
msgid "Create NAS"
msgstr "Создание NAS"
#: models.py:102
msgid "Delete NAS"
msgstr "Удаление NAS"
#: models.py:103
msgid "Create service"
msgstr "Создание тарифа"
#: models.py:104
msgid "Delete service"
msgstr "Удаление тарифа"
#: models.py:106
msgid "Action type"
msgstr "Тип действия"
#: models.py:107
msgid "Additional info"
msgstr "Дополнительная информация"
#: models.py:108
msgid "Action date"
msgstr "Дата действия"
#: models.py:115
msgid "User profile log"
msgstr "Лог действий учётной записи"
#: models.py:116
msgid "User profile logs"
msgstr "Логи действий учётной записи"
#: models.py:125
msgid "Avatar" msgid "Avatar"
msgstr "Аватар" msgstr "Аватар"
#: models.py:99
#: models.py:127
msgid "Responsibility groups" msgid "Responsibility groups"
msgstr "Группы администратора" msgstr "Группы администратора"
#: models.py:114
msgid "Can view staff profile"
msgstr "Может просматривать учётку сотрудника"
#: models.py:116
#: models.py:141
msgid "Staff account profile" msgid "Staff account profile"
msgstr "Учётная запись работника" msgstr "Учётная запись работника"
#: models.py:117
#: models.py:142
msgid "Staff account profiles" msgid "Staff account profiles"
msgstr "Учётные записи работников" msgstr "Учётные записи работников"
@ -104,6 +160,22 @@ msgstr "Пользователи не найдены"
msgid "Add account" msgid "Add account"
msgstr "Добавить учётную запись" msgstr "Добавить учётную запись"
#: templates/accounts/action_log.html:9
msgid "Date"
msgstr "Дата"
#: templates/accounts/action_log.html:10
msgid "Additional"
msgstr "Дополнительное"
#: templates/accounts/action_log.html:11
msgid "Description"
msgstr "Описание"
#: templates/accounts/action_log.html:24
msgid "That admin has no logs"
msgstr "Эта учётная запись не имеет логов"
#: templates/accounts/create_acc.html:9 #: templates/accounts/create_acc.html:9
msgid "Add" msgid "Add"
msgstr "Добавить" msgstr "Добавить"
@ -145,7 +217,7 @@ msgstr "Повторите пароль"
msgid "Save" msgid "Save"
msgstr "Сохранить" msgstr "Сохранить"
#: templates/accounts/create_acc.html:92 templates/accounts/login.html:59
#: templates/accounts/create_acc.html:92
#: templates/accounts/manage_responsibility_groups.html:21 #: templates/accounts/manage_responsibility_groups.html:21
#: templates/accounts/perms/perms_edit.html:68 #: templates/accounts/perms/perms_edit.html:68
#: templates/accounts/set_abon_groups_permission.html:21 #: templates/accounts/set_abon_groups_permission.html:21
@ -153,8 +225,7 @@ msgstr "Сохранить"
msgid "Reset" msgid "Reset"
msgstr "Сбросить" msgstr "Сбросить"
#: templates/accounts/index.html:13 templates/accounts/login.html:39
#: templates/accounts/settings/ch_info.html:9
#: templates/accounts/index.html:13 templates/accounts/settings/ch_info.html:9
#: templates/accounts/settings/ch_info.html:13 #: templates/accounts/settings/ch_info.html:13
msgid "User name" msgid "User name"
msgstr "Логин" msgstr "Логин"
@ -179,13 +250,13 @@ msgstr "Административный доступ (все права)"
msgid "Auth" msgid "Auth"
msgstr "Аутентификация" msgstr "Аутентификация"
#: templates/accounts/login.html:27 templates/accounts/login.html:56
#: templates/accounts/login.html:27 templates/accounts/login.html:43
msgid "Login" msgid "Login"
msgstr "Войти" msgstr "Войти"
#: templates/accounts/login.html:47
msgid "Password"
msgstr "Пароль"
#: templates/accounts/login.html:48
msgid "Login by location"
msgstr "Войти по местоположению"
#: templates/accounts/manage_responsibility_groups.html:5 #: templates/accounts/manage_responsibility_groups.html:5
msgid "The responsibility of the administrator of the group of subscribers" msgid "The responsibility of the administrator of the group of subscribers"
@ -235,50 +306,53 @@ msgstr "Старый пароль"
msgid "New password" msgid "New password"
msgstr "Новый пароль" msgstr "Новый пароль"
#: views.py:47
#: views.py:32
msgid "Wrong login or password, please try again" msgid "Wrong login or password, please try again"
msgstr "Неправильный логин или пароль, попробуйте ещё раз" msgstr "Неправильный логин или пароль, попробуйте ещё раз"
#: views.py:123
#: views.py:120
msgid "New password is empty, fill it" msgid "New password is empty, fill it"
msgstr "Новый пароль пустой, придумайте себе пароль" msgstr "Новый пароль пустой, придумайте себе пароль"
#: views.py:125
#: views.py:122
msgid "Wrong password" msgid "Wrong password"
msgstr "Неправильный пароль" msgstr "Неправильный пароль"
#: views.py:127
#: views.py:124
msgid "Empty password, fill it" msgid "Empty password, fill it"
msgstr "Пустой пароль, впишите что-то в пароль" msgstr "Пустой пароль, впишите что-то в пароль"
#: views.py:150
#: views.py:148
msgid "You forget specify a password for the new account" msgid "You forget specify a password for the new account"
msgstr "Забыли указать пароль для нового аккаунта" msgstr "Забыли указать пароль для нового аккаунта"
#: views.py:153
#: views.py:151
msgid "You forget to repeat a password for the new account" msgid "You forget to repeat a password for the new account"
msgstr "Забыли повторить пароль для нового аккаунта" msgstr "Забыли повторить пароль для нового аккаунта"
#: views.py:162
#: views.py:160
msgid "Subscriber with this name already exist" msgid "Subscriber with this name already exist"
msgstr "Пользователь с таким именем уже есть" msgstr "Пользователь с таким именем уже есть"
#: views.py:164
#: views.py:162
msgid "Passwords does not match, try again" msgid "Passwords does not match, try again"
msgstr "Пароли не совпадают, попробуйте ещё раз" msgstr "Пароли не совпадают, попробуйте ещё раз"
#: views.py:179
#: views.py:177
msgid "Profile has been deleted" msgid "Profile has been deleted"
msgstr "Учётная запись удалена" msgstr "Учётная запись удалена"
#: views.py:252
#: views.py:255
msgid "Permissions has successfully updated" msgid "Permissions has successfully updated"
msgstr "Права успешно обновлены" msgstr "Права успешно обновлены"
#: views.py:318
#: views.py:322
msgid "Responsibilities has been updated" msgid "Responsibilities has been updated"
msgstr "Ответственность за группы обновлена" msgstr "Ответственность за группы обновлена"
msgid "Password"
msgstr "Пароль"
msgid "Change self onfo" msgid "Change self onfo"
msgstr "Изменить инфу о себе" msgstr "Изменить инфу о себе"
@ -297,5 +371,5 @@ msgstr "Ответственность за группы"
msgid "Profile" msgid "Profile"
msgstr "Учётная запись" msgstr "Учётная запись"
msgid "Login by location"
msgstr "Войти по местоположению"
msgid "Action log"
msgstr "Лог действий"

45
accounts_app/migrations/0003_new_user_profile_log.py

@ -0,0 +1,45 @@
# Generated by Django 2.1 on 2018-08-26 19:52
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import jsonfield.fields
class Migration(migrations.Migration):
dependencies = [
('accounts_app', '0002_auto_20180807_1548'),
]
operations = [
migrations.CreateModel(
name='UserProfileLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('meta_info', jsonfield.fields.JSONField(default=dict, verbose_name='Meta information')),
('do_type', models.CharField(choices=[('cusr', 'Create user'), ('dusr', 'Delete user'), ('cdev', 'Create device'), ('ddev', 'Delete device'), ('cnas', 'Create NAS'), ('dnas', 'Delete NAS'), ('csrv', 'Create service'), ('dsrv', 'Delete service')], max_length=4, verbose_name='Action type')),
('additional_text', models.CharField(blank=True, null=True, verbose_name='Additional info', max_length=512)),
('action_date', models.DateTimeField(auto_now_add=True, verbose_name='Action date')),
],
options={
'verbose_name': 'User profile log',
'verbose_name_plural': 'User profile logs',
'ordering': ('-action_date',),
},
),
migrations.AlterModelOptions(
name='userprofile',
options={'ordering': ('fio',), 'verbose_name': 'Staff account profile', 'verbose_name_plural': 'Staff account profiles'},
),
migrations.AlterField(
model_name='userprofile',
name='avatar',
field=models.ImageField(blank=True, default=None, null=True, upload_to='user/avatar', verbose_name='Avatar'),
),
migrations.AddField(
model_name='userprofilelog',
name='account',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Author'),
),
]

56
accounts_app/models.py

@ -1,6 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
import os import os
from PIL import Image from PIL import Image
from jsonfield import JSONField
from django.db import models from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
@ -85,7 +87,33 @@ class BaseAccount(AbstractBaseUser, PermissionsMixin):
class Meta: class Meta:
db_table = 'base_accounts' db_table = 'base_accounts'
ordering = ('username',)
ordering = 'username',
class UserProfileLog(models.Model):
account = models.ForeignKey('UserProfile', on_delete=models.CASCADE, verbose_name=_('Author'))
meta_info = JSONField(verbose_name=_('Meta information'))
ACTION_TYPES = (
('cusr', _('Create user')),
('dusr', _('Delete user')),
('cdev', _('Create device')),
('ddev', _('Delete device')),
('cnas', _('Create NAS')),
('dnas', _('Delete NAS')),
('csrv', _('Create service')),
('dsrv', _('Delete service'))
)
do_type = models.CharField(_('Action type'), max_length=4, choices=ACTION_TYPES)
additional_text = models.CharField(_('Additional info'), blank=True, null=True, max_length=512)
action_date = models.DateTimeField(_('Action date'), auto_now_add=True)
def __str__(self):
return self.get_do_type_display()
class Meta:
ordering = '-action_date',
verbose_name = _('User profile log')
verbose_name_plural = _('User profile logs')
class UserProfileManager(MyUserManager): class UserProfileManager(MyUserManager):
@ -110,12 +138,9 @@ class UserProfile(BaseAccount):
return self.get_big_ava() return self.get_big_ava()
class Meta: class Meta:
permissions = (
('can_view_userprofile', _('Can view staff profile')),
)
verbose_name = _('Staff account profile') verbose_name = _('Staff account profile')
verbose_name_plural = _('Staff account profiles') verbose_name_plural = _('Staff account profiles')
ordering = ('fio',)
ordering = 'fio',
def _thumbnail_avatar(self): def _thumbnail_avatar(self):
if self.avatar and os.path.isfile(self.avatar.path): if self.avatar and os.path.isfile(self.avatar.path):
@ -127,3 +152,24 @@ class UserProfile(BaseAccount):
r = super().save(*args, **kwargs) r = super().save(*args, **kwargs)
self._thumbnail_avatar() self._thumbnail_avatar()
return r return r
def log(self, request_meta: dict, do_type: str, additional_text=None) -> None:
"""
Make log about administrator actions.
:param request_meta: META from django request.
:param do_type: Choice from UserProfileLog.ACTION_TYPES
:param additional_text: Additional information for action
:return: None
"""
inf = {
'src_ip': request_meta.get('REMOTE_ADDR'),
'username': request_meta.get('USER'),
'hostname': request_meta.get('HOSTNAME'),
'useragent': request_meta.get('HTTP_USER_AGENT')
}
UserProfileLog.objects.create(
account=self,
meta_info=inf,
do_type=do_type,
additional_text=additional_text
)

29
accounts_app/templates/accounts/action_log.html

@ -0,0 +1,29 @@
{% extends request.is_ajax|yesno:'nullcont.htm,accounts/ext.htm' %}
{% load i18n %}
{% block content %}
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>{% trans 'Date' %}</th>
<th>{% trans 'Additional' %}</th>
<th>{% trans 'Description' %}</th>
<th>{% trans 'Meta information' %}</th>
</tr>
</thead>
<tbody>
{% for log in object_list %}
<tr>
<td>{{ log.action_date|date:'D d E Y H:i:s' }}</td>
<td>{{ log.additional_text|default_if_none:'-' }}</td>
<td>{{ log.get_do_type_display }}</td>
<td>{{ log.meta_info }}</td>
</tr>
{% empty %}
<tr><td colspan="4">{% trans 'That admin has no logs' %}</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

11
accounts_app/templates/accounts/ext.htm

@ -70,6 +70,17 @@
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if perms.accounts_app.view_userprofilelog %}
{% url 'acc_app:action_log' uid as actlog %}
<li{% if actlog == request.path %} class="active"{% endif %}>
<a href="{{ actlog }}">
<span class="glyphicon glyphicon-book"></span>
{% trans 'Action log' %}
</a>
</li>
{% endif %}
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active"> <div class="tab-pane active">

4
accounts_app/urls.py

@ -37,5 +37,7 @@ urlpatterns = [
path('<int:uid>/manage_responsibility_groups/', path('<int:uid>/manage_responsibility_groups/',
views.ManageResponsibilityGroups.as_view(), views.ManageResponsibilityGroups.as_view(),
name='manage_responsibility_groups')
name='manage_responsibility_groups'),
path('<int:uid>/actions/', views.ActionListView.as_view(), name='action_log')
] ]

32
accounts_app/views.py

@ -14,7 +14,7 @@ from django.conf import settings
from group_app.models import Group from group_app.models import Group
from .models import UserProfile
from .models import UserProfile, UserProfileLog
from .forms import AvatarChangeForm from .forms import AvatarChangeForm
from djing import lib from djing import lib
from djing.lib.decorators import only_admins from djing.lib.decorators import only_admins
@ -67,7 +67,7 @@ def profile_show(request, uid=0):
return redirect('acc_app:other_profile', uid=request.user.id) return redirect('acc_app:other_profile', uid=request.user.id)
usr = get_object_or_404(UserProfile, id=uid) usr = get_object_or_404(UserProfile, id=uid)
if request.user != usr and not request.user.has_perm('accounts_app.can_view_userprofile', usr):
if request.user != usr and not request.user.has_perm('accounts_app.view_userprofile', usr):
raise PermissionDenied raise PermissionDenied
if request.method == 'POST': if request.method == 'POST':
usr.username = request.POST.get('username') usr.username = request.POST.get('username')
@ -168,7 +168,7 @@ def create_profile(request):
@login_required @login_required
@only_admins @only_admins
def delete_profile(request, uid):
def delete_profile(request, uid: int):
prf = get_object_or_404(UserProfile, id=uid) prf = get_object_or_404(UserProfile, id=uid)
if uid != request.user.id: if uid != request.user.id:
if not request.user.has_perm('acc_app.delete_userprofile', prf): if not request.user.has_perm('acc_app.delete_userprofile', prf):
@ -187,12 +187,12 @@ class AccountsListView(ListView):
def get_queryset(self): def get_queryset(self):
users = UserProfile.objects.filter(is_admin=True).exclude(pk=self.request.user.pk) users = UserProfile.objects.filter(is_admin=True).exclude(pk=self.request.user.pk)
users = get_objects_for_user(self.request.user, 'accounts_app.can_view_userprofile', users)
users = get_objects_for_user(self.request.user, 'accounts_app.view_userprofile', users)
return users return users
@login_required @login_required
def perms(request, uid):
def perms(request, uid: int):
if not request.user.is_superuser: if not request.user.is_superuser:
raise PermissionDenied raise PermissionDenied
userprofile = get_object_or_404(UserProfile, id=uid) userprofile = get_object_or_404(UserProfile, id=uid)
@ -239,7 +239,7 @@ class PermissionClassListView(ListView):
@login_required @login_required
@only_admins @only_admins
def perms_edit(request, uid, klass_name, obj_id):
def perms_edit(request, uid: int, klass_name, obj_id):
if not request.user.is_superuser: if not request.user.is_superuser:
raise PermissionDenied raise PermissionDenied
from django.apps import apps from django.apps import apps
@ -265,7 +265,7 @@ def perms_edit(request, uid, klass_name, obj_id):
@login_required @login_required
@only_admins @only_admins
def set_abon_groups_permission(request, uid):
def set_abon_groups_permission(request, uid: int):
# Only superuser can change object permissions # Only superuser can change object permissions
if not request.user.is_superuser: if not request.user.is_superuser:
raise PermissionDenied raise PermissionDenied
@ -321,3 +321,21 @@ class ManageResponsibilityGroups(ListView):
profile.responsibility_groups.add(*checked_groups) profile.responsibility_groups.add(*checked_groups)
messages.success(request, _('Responsibilities has been updated')) messages.success(request, _('Responsibilities has been updated'))
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
@method_decorator(login_decs, name='dispatch')
@method_decorator(permission_required('accounts_app.view_userprofilelog'), name='dispatch')
class ActionListView(ListView):
paginate_by = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10)
template_name = 'accounts/action_log.html'
model = UserProfileLog
def get_queryset(self):
uid = self.kwargs.get('uid')
return UserProfileLog.objects.filter(account__id=uid)
def get_context_data(self, **kwargs):
context = super(ActionListView, self).get_context_data(**kwargs)
context['uid'] = self.kwargs.get('uid')
context['userprofile'] = UserProfile.objects.get(pk=context['uid'])
return context

1
devapp/forms.py

@ -22,6 +22,7 @@ class DeviceForm(forms.ModelForm):
else: else:
comment = None comment = None
super(DeviceForm, self).__init__(*args, **kwargs) super(DeviceForm, self).__init__(*args, **kwargs)
if comment:
self.fields['comment'].widget.attrs['placeholder'] = comment self.fields['comment'].widget.attrs['placeholder'] = comment
mac_addr = forms.CharField(widget=forms.TextInput(attrs={ mac_addr = forms.CharField(widget=forms.TextInput(attrs={

12
devapp/views.py

@ -79,8 +79,13 @@ class DeviceDeleteView(DeleteView):
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
res = super().delete(request, *args, **kwargs) res = super().delete(request, *args, **kwargs)
try: try:
request.user.log(request.META, 'ddev', 'ip %s, mac: %s, "%s"' % (
self.object.ip_address or '-',
self.object.mac_addr or '-',
self.object.comment or '-'
))
self.object.update_dhcp() self.object.update_dhcp()
except DeviceDBException as e:
except (DeviceDBException, PermissionError) as e:
messages.error(request, e) messages.error(request, e)
messages.success(request, _('Device successfully deleted')) messages.success(request, _('Device successfully deleted'))
return res return res
@ -183,6 +188,11 @@ class DeviceCreateView(CreateView):
r = super().form_valid(form) r = super().form_valid(form)
# change device info in dhcpd.conf # change device info in dhcpd.conf
try: try:
self.request.user.log(self.request.META, 'cdev', 'ip %s, mac: %s, "%s"' % (
self.object.ip_address,
self.object.mac_addr,
self.object.comment
))
self.object.update_dhcp() self.object.update_dhcp()
messages.success(self.request, _('Device info has been saved')) messages.success(self.request, _('Device info has been saved'))
except PermissionError as e: except PermissionError as e:

2
msg_app/templates/msg_app/chat.html

@ -25,7 +25,7 @@
</div> </div>
<div class="list-group scroll-area"> <div class="list-group scroll-area">
{% with can_view_profile=perms.accounts_app.can_view_userprofile %}
{% with can_view_profile=perms.accounts_app.view_userprofile %}
{% for msg in msg_list %} {% for msg in msg_list %}
{% with author=msg.author %} {% with author=msg.author %}
<div class="list-group-item"> <div class="list-group-item">

10
nas_app/views.py

@ -34,6 +34,11 @@ class NasCreateView(CreateView):
r = super(NasCreateView, self).form_valid(form) r = super(NasCreateView, self).form_valid(form)
assign_perm("nas_app.change_nasmodel", self.request.user, self.object) assign_perm("nas_app.change_nasmodel", self.request.user, self.object)
assign_perm("nas_app.can_view_nas", self.request.user, self.object) assign_perm("nas_app.can_view_nas", self.request.user, self.object)
self.request.user.log(self.request.META, 'cnas', '"%(title)s", %(ip)s, %(type)s' % {
'title': self.object.title,
'ip': self.object.ip_address,
'type': self.object.get_nas_type_display()
})
messages.success(self.request, _('New NAS has been created')) messages.success(self.request, _('New NAS has been created'))
return r return r
@ -48,6 +53,11 @@ class NasDeleteView(DeleteView):
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
try: try:
r = super(NasDeleteView, self).delete(request, *args, **kwargs) r = super(NasDeleteView, self).delete(request, *args, **kwargs)
request.user.log(request.META, 'dnas', '"%(title)s", %(ip)s, %(type)s' % {
'title': self.object.title,
'ip': self.object.ip_address,
'type': self.object.get_nas_type_display()
})
messages.success(request, _('Server successfully removed')) messages.success(request, _('Server successfully removed'))
return r return r
except MessageFailure as e: except MessageFailure as e:

15
tariff_app/views.py

@ -47,9 +47,15 @@ def edit_tarif(request, tarif_id=0):
if request.method == 'POST': if request.method == 'POST':
frm = forms.TariffForm(request.POST, instance=tarif) frm = forms.TariffForm(request.POST, instance=tarif)
if frm.is_valid(): if frm.is_valid():
new_service = frm.save()
service = frm.save()
if tarif is None:
request.user.log(request.META, 'csrv', '"%(title)s", "%(descr)s", %(amount).2f' % {
'title': service.title or '-',
'descr': service.descr or '-',
'amount': service.amount or 0.0
})
messages.success(request, _('Service has been saved')) messages.success(request, _('Service has been saved'))
return redirect('tarifs:edit', tarif_id=new_service.pk)
return redirect('tarifs:edit', tarif_id=service.pk)
else: else:
messages.warning(request, _('Some fields were filled incorrect, please try again')) messages.warning(request, _('Some fields were filled incorrect, please try again'))
else: else:
@ -70,6 +76,11 @@ class TariffDeleteView(DeleteView):
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
res = super().delete(request, *args, **kwargs) res = super().delete(request, *args, **kwargs)
request.user.log(request.META, 'dsrv', '"%(title)s", "%(descr)s", %(amount).2f' % {
'title': self.object.title or '-',
'descr': self.object.descr or '-',
'amount': self.object.amount or 0.0
})
messages.success(request, _('Service has been deleted')) messages.success(request, _('Service has been deleted'))
return res return res

Loading…
Cancel
Save