Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
f37b3316
Commit
f37b3316
authored
Jan 23, 2018
by
ibuler
Browse files
Options
Browse Files
Download
Plain Diff
Merge with dev
parents
5bbad019
9dbf4983
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
69 changed files
with
552 additions
and
302 deletions
+552
-302
README.md
README.md
+1
-30
api.py
apps/assets/api.py
+3
-5
hands.py
apps/assets/hands.py
+1
-1
label.py
apps/assets/models/label.py
+1
-1
user.py
apps/assets/models/user.py
+0
-1
serializers.py
apps/assets/serializers.py
+1
-11
admin_user_assets.html
apps/assets/templates/assets/admin_user_assets.html
+1
-1
asset_group_detail.html
apps/assets/templates/assets/asset_group_detail.html
+1
-1
asset_group_list.html
apps/assets/templates/assets/asset_group_list.html
+0
-4
cluster_assets.html
apps/assets/templates/assets/cluster_assets.html
+1
-1
label_list.html
apps/assets/templates/assets/label_list.html
+2
-5
system_user_asset.html
apps/assets/templates/assets/system_user_asset.html
+1
-1
cluster.py
apps/assets/views/cluster.py
+0
-5
api.py
apps/common/api.py
+0
-5
fields.py
apps/common/fields.py
+6
-5
forms.py
apps/common/forms.py
+38
-10
models.py
apps/common/models.py
+16
-3
basic_setting.html
apps/common/templates/common/basic_setting.html
+3
-0
email_setting.html
apps/common/templates/common/email_setting.html
+3
-0
ldap_setting.html
apps/common/templates/common/ldap_setting.html
+3
-0
terminal_setting.html
apps/common/templates/common/terminal_setting.html
+129
-0
view_urls.py
apps/common/urls/view_urls.py
+1
-0
views.py
apps/common/views.py
+35
-4
settings.py
apps/jumpserver/settings.py
+16
-3
django.mo
apps/locale/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/locale/zh/LC_MESSAGES/django.po
+0
-0
models.py
apps/ops/models.py
+1
-1
utils.py
apps/ops/utils.py
+3
-1
jumpserver.css
apps/static/css/jumpserver.css
+1
-1
admin.png
apps/static/img/avatar/admin.png
+0
-0
user.png
apps/static/img/avatar/user.png
+0
-0
logo-text.png
apps/static/img/logo-text.png
+0
-0
_footer.html
apps/templates/_footer.html
+2
-1
_header_bar.html
apps/templates/_header_bar.html
+6
-7
_user_profile.html
apps/templates/_user_profile.html
+2
-5
flash_message_standalone.html
apps/templates/flash_message_standalone.html
+1
-1
index.html
apps/templates/index.html
+1
-1
api.py
apps/terminal/api.py
+17
-4
__init__.py
apps/terminal/backends/__init__.py
+33
-3
base.py
apps/terminal/backends/command/base.py
+6
-0
db.py
apps/terminal/backends/command/db.py
+26
-6
es.py
apps/terminal/backends/command/es.py
+38
-0
models.py
apps/terminal/backends/command/models.py
+21
-0
multi.py
apps/terminal/backends/command/multi.py
+27
-0
forms.py
apps/terminal/forms.py
+13
-1
models.py
apps/terminal/models.py
+23
-0
serializers.py
apps/terminal/serializers.py
+3
-3
signals_handler.py
apps/terminal/signals_handler.py
+0
-4
terminal_modal_accept.html
apps/terminal/templates/terminal/terminal_modal_accept.html
+1
-0
terminal_update.html
apps/terminal/templates/terminal/terminal_update.html
+1
-0
terminal_tags.py
apps/terminal/templatetags/terminal_tags.py
+3
-4
api_urls.py
apps/terminal/urls/api_urls.py
+2
-1
command.py
apps/terminal/views/command.py
+3
-3
session.py
apps/terminal/views/session.py
+2
-2
api.py
apps/users/api.py
+20
-4
forms.py
apps/users/forms.py
+0
-1
hands.py
apps/users/hands.py
+1
-1
serializers.py
apps/users/serializers.py
+6
-0
_user.html
apps/users/templates/users/_user.html
+1
-7
forgot_password.html
apps/users/templates/users/forgot_password.html
+1
-1
login.html
apps/users/templates/users/login.html
+1
-1
reset_password.html
apps/users/templates/users/reset_password.html
+1
-1
user_detail.html
apps/users/templates/users/user_detail.html
+7
-6
user_list.html
apps/users/templates/users/user_list.html
+1
-1
api_urls.py
apps/users/urls/api_urls.py
+2
-0
utils.py
apps/users/utils.py
+3
-1
login.py
apps/users/views/login.py
+5
-2
install.md
docs/install.md
+1
-129
requirements.txt
requirements/requirements.txt
+3
-1
No files found.
README.md
View file @
f37b3316
...
...
@@ -26,36 +26,7 @@ Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互
### Install 安装
1. 安装 Python3
略
2. 安装依赖
```
$ cd requirements && yum -y install $(cat rpm_requirements.txt) && pip install -r requirements.txt
```
3. 修改配置文件
```
$ cp config_example.py config.py
```
4. 修改表结构
```
$ cd apps && python manage.py makemigrations && python manage.py migrate
```
5. 运行
```
$ python run_server.py
```
6. 其它
整合luna,coco需要nginx来配合, 详见详细安装文档
[
详细安装
](
https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7
)
### Usage 使用
...
...
apps/assets/api.py
View file @
f37b3316
# ~*~ coding: utf-8 ~*~
# Copyright (C) 2014-201
7
Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
# Copyright (C) 2014-201
8
Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved.
#
# Licensed under the GNU General Public License v2.0 (the "License");
# you may not use this file except in compliance with the License.
...
...
@@ -87,7 +87,7 @@ class AssetGroupViewSet(CustomFilterMixin, BulkModelViewSet):
"""
Asset group api set, for add,delete,update,list,retrieve resource
"""
queryset
=
AssetGroup
.
objects
.
all
()
queryset
=
AssetGroup
.
objects
.
all
()
.
annotate
(
asset_count
=
Count
(
"assets"
))
serializer_class
=
serializers
.
AssetGroupSerializer
permission_classes
=
(
IsSuperUser
,)
...
...
@@ -298,9 +298,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
class
LabelViewSet
(
BulkModelViewSet
):
queryset
=
Label
.
objects
.
annotate
(
asset_count
=
Count
(
"assets"
))
\
.
annotate
(
admin_user_count
=
Count
(
"adminuser"
))
\
.
annotate
(
system_user_count
=
Count
(
"systemuser"
))
queryset
=
Label
.
objects
.
annotate
(
asset_count
=
Count
(
"assets"
))
permission_classes
=
(
IsSuperUser
,)
serializer_class
=
serializers
.
LabelSerializer
...
...
apps/assets/hands.py
View file @
f37b3316
...
...
@@ -6,7 +6,7 @@
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-201
7
by Jumpserver Team.
:copyright: (c) 2014-201
8
by Jumpserver Team.
:license: GPL v2, see LICENSE for more details.
"""
...
...
apps/assets/models/label.py
View file @
f37b3316
...
...
@@ -16,7 +16,7 @@ class Label(models.Model):
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
name
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Name"
))
value
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Value"
))
category
=
models
.
CharField
(
max_length
=
128
,
choices
=
CATEGORY_CHOICES
,
verbose_name
=
_
(
"Category"
))
category
=
models
.
CharField
(
max_length
=
128
,
choices
=
CATEGORY_CHOICES
,
default
=
USER_CATEGORY
,
verbose_name
=
_
(
"Category"
))
is_active
=
models
.
BooleanField
(
default
=
True
,
verbose_name
=
_
(
"Is active"
))
comment
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
"Comment"
))
date_created
=
models
.
DateTimeField
(
...
...
apps/assets/models/user.py
View file @
f37b3316
...
...
@@ -30,7 +30,6 @@ class AssetUser(models.Model):
_password
=
models
.
CharField
(
max_length
=
256
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Password'
))
_private_key
=
models
.
TextField
(
max_length
=
4096
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'SSH private key'
),
validators
=
[
private_key_validator
,
])
_public_key
=
models
.
TextField
(
max_length
=
4096
,
blank
=
True
,
verbose_name
=
_
(
'SSH public key'
))
labels
=
models
.
ManyToManyField
(
'assets.Label'
,
blank
=
True
,
verbose_name
=
_
(
"Labels"
))
comment
=
models
.
TextField
(
blank
=
True
,
verbose_name
=
_
(
'Comment'
))
date_created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
date_updated
=
models
.
DateTimeField
(
auto_now
=
True
)
...
...
apps/assets/serializers.py
View file @
f37b3316
...
...
@@ -22,7 +22,7 @@ class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
@staticmethod
def
get_assets_amount
(
obj
):
return
obj
.
asset
s
.
count
()
return
obj
.
asset
_count
class
AssetUpdateSystemUserSerializer
(
serializers
.
ModelSerializer
):
...
...
@@ -288,8 +288,6 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer):
class
LabelSerializer
(
serializers
.
ModelSerializer
):
asset_count
=
serializers
.
SerializerMethodField
()
admin_user_count
=
serializers
.
SerializerMethodField
()
system_user_count
=
serializers
.
SerializerMethodField
()
class
Meta
:
model
=
Label
...
...
@@ -300,14 +298,6 @@ class LabelSerializer(serializers.ModelSerializer):
def
get_asset_count
(
obj
):
return
obj
.
asset_count
@staticmethod
def
get_admin_user_count
(
obj
):
return
obj
.
admin_user_count
@staticmethod
def
get_system_user_count
(
obj
):
return
obj
.
system_user_count
def
get_field_names
(
self
,
declared_fields
,
info
):
fields
=
super
()
.
get_field_names
(
declared_fields
,
info
)
fields
.
extend
([
'get_category_display'
])
...
...
apps/assets/templates/assets/admin_user_assets.html
View file @
f37b3316
...
...
@@ -121,7 +121,7 @@ function initTable() {
{
data
:
"type"
},
{
data
:
"is_connective"
}],
op_html
:
$
(
'#actions'
).
html
()
};
jumpserver
.
initDataTable
(
options
);
jumpserver
.
init
ServerSide
DataTable
(
options
);
}
$
(
document
).
ready
(
function
()
{
...
...
apps/assets/templates/assets/asset_group_detail.html
View file @
f37b3316
...
...
@@ -184,7 +184,7 @@ function initTable() {
{
data
:
"get_type_display"
},
{
data
:
"is_connective"
},
{
data
:
"id"
}],
op_html
:
$
(
'#actions'
).
html
()
};
jumpserver
.
initDataTable
(
options
);
jumpserver
.
init
ServerSide
DataTable
(
options
);
}
...
...
apps/assets/templates/assets/asset_group_list.html
View file @
f37b3316
...
...
@@ -34,10 +34,6 @@ $(document).ready(function(){
var
detail_btn
=
'<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">'
+
cellData
+
'</a>'
;
$
(
td
).
html
(
detail_btn
.
replace
(
'{{ DEFAULT_PK }}'
,
rowData
.
id
));
}},
{
targets
:
3
,
createdCell
:
function
(
td
,
cellData
)
{
var
innerHtml
=
cellData
.
length
>
30
?
cellData
.
substring
(
0
,
30
)
+
'...'
:
cellData
;
$
(
td
).
html
(
'<a href="javascript:void(0);" data-toggle="tooltip" title="'
+
cellData
+
'">'
+
innerHtml
+
'</a>'
);
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
update_btn
=
'<a href="{% url "assets:asset-group-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
var
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs btn_asset_group_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
...
...
apps/assets/templates/assets/cluster_assets.html
View file @
f37b3316
...
...
@@ -176,7 +176,7 @@ function initTable() {
{
data
:
"get_type_display"
},
{
data
:
"is_connective"
},
{
data
:
"id"
}],
op_html
:
$
(
'#actions'
).
html
()
};
jumpserver
.
initDataTable
(
options
);
jumpserver
.
init
ServerSide
DataTable
(
options
);
}
...
...
apps/assets/templates/assets/label_list.html
View file @
f37b3316
...
...
@@ -14,8 +14,6 @@
<th
class=
"text-center"
>
{% trans 'Name' %}
</th>
<th
class=
"text-center"
>
{% trans 'Value' %}
</th>
<th
class=
"text-center"
>
{% trans 'Asset' %}
</th>
<th
class=
"text-center"
>
{% trans 'Admin user' %}
</th>
<th
class=
"text-center"
>
{% trans 'System user' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
</tr>
</thead>
...
...
@@ -36,7 +34,7 @@ function initTable() {
$
(
td
).
html
(
detail_btn
.
replace
(
'{{ DEFAULT_PK }}'
,
rowData
.
id
));
}},
{
targets
:
6
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
update_btn
=
'<a href="{% url "assets:cluster-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
var
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs btn_cluster_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
);
$
(
td
).
html
(
update_btn
+
del_btn
)
...
...
@@ -44,8 +42,7 @@ function initTable() {
ajax_url
:
'{% url "api-assets:label-list" %}?sort=name'
,
columns
:
[
{
data
:
"id"
},
{
data
:
"name"
},
{
data
:
"value"
},
{
data
:
"asset_count"
},
{
data
:
"admin_user_count"
},
{
data
:
"system_user_count"
},
{
data
:
"id"
}
{
data
:
"asset_count"
},
{
data
:
"id"
}
],
op_html
:
$
(
'#actions'
).
html
()
};
...
...
apps/assets/templates/assets/system_user_asset.html
View file @
f37b3316
...
...
@@ -121,7 +121,7 @@ function initAssetsTable() {
columns
:
[{
data
:
"hostname"
},
{
data
:
"ip"
},
{
data
:
"port"
},
{
data
:
"hostname"
}],
op_html
:
$
(
'#actions'
).
html
()
};
jumpserver
.
initDataTable
(
options
);
jumpserver
.
init
ServerSide
DataTable
(
options
);
}
$
(
document
).
ready
(
function
()
{
...
...
apps/assets/views/cluster.py
View file @
f37b3316
...
...
@@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView)
success_url
=
reverse_lazy
(
'assets:cluster-list'
)
success_message
=
update_success_msg
def
form_valid
(
self
,
form
):
cluster
=
form
.
save
(
commit
=
False
)
cluster
.
save
()
return
super
()
.
form_valid
(
form
)
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'assets'
),
...
...
apps/common/api.py
View file @
f37b3316
...
...
@@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView):
search_filter
=
serializer
.
validated_data
[
"AUTH_LDAP_SEARCH_FILTER"
]
attr_map
=
serializer
.
validated_data
[
"AUTH_LDAP_USER_ATTR_MAP"
]
print
(
serializer
.
validated_data
)
try
:
attr_map
=
json
.
loads
(
attr_map
)
except
json
.
JSONDecodeError
:
...
...
@@ -77,9 +75,6 @@ class LDAPTestingAPI(APIView):
except
Exception
as
e
:
return
Response
({
"error"
:
str
(
e
)},
status
=
401
)
print
(
search_ou
)
print
(
search_filter
%
({
"user"
:
"*"
}))
print
(
attr_map
.
values
())
ok
=
conn
.
search
(
search_ou
,
search_filter
%
({
"user"
:
"*"
}),
attributes
=
list
(
attr_map
.
values
()))
if
not
ok
:
...
...
apps/common/fields.py
View file @
f37b3316
...
...
@@ -5,6 +5,7 @@ import json
from
django
import
forms
from
django.utils
import
six
from
django.core.exceptions
import
ValidationError
from
django.utils.translation
import
ugettext
as
_
class
DictField
(
forms
.
Field
):
...
...
@@ -18,16 +19,16 @@ class DictField(forms.Field):
# we don't need to handle that explicitly.
if
isinstance
(
value
,
six
.
string_types
):
try
:
print
(
value
)
value
=
json
.
loads
(
value
)
return
value
except
json
.
JSONDecodeError
:
pass
value
=
{}
return
value
return
ValidationError
(
_
(
"Not a valid json"
))
else
:
return
ValidationError
(
_
(
"Not a string type"
))
def
validate
(
self
,
value
):
print
(
value
)
if
isinstance
(
value
,
ValidationError
):
raise
value
if
not
value
and
self
.
required
:
raise
ValidationError
(
self
.
error_messages
[
'required'
],
code
=
'required'
)
...
...
apps/common/forms.py
View file @
f37b3316
...
...
@@ -4,7 +4,9 @@ import json
from
django
import
forms
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.html
import
escape
from
django.db
import
transaction
from
django.conf
import
settings
from
.models
import
Setting
from
.fields
import
DictField
...
...
@@ -24,34 +26,38 @@ def to_form_value(value):
data
=
value
return
data
except
json
.
JSONDecodeError
:
return
''
return
""
class
BaseForm
(
forms
.
Form
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
()
.
__init__
(
*
args
,
**
kwargs
)
settings
=
Setting
.
objects
.
all
()
db_
settings
=
Setting
.
objects
.
all
()
for
name
,
field
in
self
.
fields
.
items
():
db_value
=
getattr
(
settings
,
name
)
.
value
if
db_value
:
db_value
=
getattr
(
db_settings
,
name
)
.
value
django_value
=
getattr
(
settings
,
name
)
if
hasattr
(
settings
,
name
)
else
None
if
db_value
is
False
or
db_value
:
field
.
initial
=
to_form_value
(
db_value
)
elif
django_value
is
False
or
django_value
:
field
.
initial
=
to_form_value
(
to_model_value
(
django_value
))
def
save
(
self
):
def
save
(
self
,
category
=
"default"
):
if
not
self
.
is_bound
:
raise
ValueError
(
"Form is not bound"
)
settings
=
Setting
.
objects
.
all
()
db_
settings
=
Setting
.
objects
.
all
()
if
self
.
is_valid
():
with
transaction
.
atomic
():
for
name
,
value
in
self
.
cleaned_data
.
items
():
field
=
self
.
fields
[
name
]
if
isinstance
(
field
.
widget
,
forms
.
PasswordInput
)
and
not
value
:
continue
if
value
==
to_form_value
(
getattr
(
settings
,
name
)
.
value
):
if
value
==
to_form_value
(
getattr
(
db_
settings
,
name
)
.
value
):
continue
defaults
=
{
'name'
:
name
,
'category'
:
category
,
'value'
:
to_model_value
(
value
)
}
Setting
.
objects
.
update_or_create
(
defaults
=
defaults
,
name
=
name
)
...
...
@@ -72,9 +78,6 @@ class BasicSettingForm(BaseForm):
max_length
=
1024
,
label
=
_
(
"Email Subject Prefix"
),
initial
=
"[Jumpserver] "
)
AUTH_LDAP
=
forms
.
BooleanField
(
label
=
_
(
"Enable LDAP Auth"
),
initial
=
False
,
required
=
False
)
class
EmailSettingForm
(
BaseForm
):
...
...
@@ -129,3 +132,28 @@ class LDAPSettingForm(BaseForm):
AUTH_LDAP_START_TLS
=
forms
.
BooleanField
(
label
=
_
(
"Use SSL"
),
initial
=
False
,
required
=
False
)
class
TerminalSettingForm
(
BaseForm
):
SORT_BY_CHOICES
=
(
(
'hostname'
,
_
(
'Hostname'
)),
(
'ip'
,
_
(
'IP'
)),
)
TERMINAL_ASSET_LIST_SORT_BY
=
forms
.
ChoiceField
(
choices
=
SORT_BY_CHOICES
,
initial
=
'hostname'
,
label
=
_
(
"List sort by"
)
)
TERMINAL_HEARTBEAT_INTERVAL
=
forms
.
IntegerField
(
initial
=
5
,
label
=
_
(
"Heartbeat interval"
),
help_text
=
_
(
"Units: seconds"
)
)
TERMINAL_PASSWORD_AUTH
=
forms
.
BooleanField
(
initial
=
True
,
required
=
False
,
label
=
_
(
"Password auth"
)
)
TERMINAL_PUBLIC_KEY_AUTH
=
forms
.
BooleanField
(
initial
=
True
,
required
=
False
,
label
=
_
(
"Public key auth"
)
)
TERMINAL_COMMAND_STORAGE
=
DictField
(
label
=
_
(
"Command storage"
),
help_text
=
_
(
"Set terminal storage setting, `default` is the using as default,"
"You can set other storage and some terminal using"
)
)
apps/common/models.py
View file @
f37b3316
...
...
@@ -2,6 +2,7 @@ import json
import
ldap
from
django.db
import
models
from
django.db.utils
import
ProgrammingError
,
OperationalError
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.conf
import
settings
from
django_auth_ldap.config
import
LDAPSearch
...
...
@@ -24,6 +25,7 @@ class SettingManager(models.Manager):
class
Setting
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
"Name"
))
value
=
models
.
TextField
(
verbose_name
=
_
(
"Value"
))
category
=
models
.
CharField
(
max_length
=
128
,
default
=
"default"
)
enabled
=
models
.
BooleanField
(
verbose_name
=
_
(
"Enabled"
),
default
=
True
)
comment
=
models
.
TextField
(
verbose_name
=
_
(
"Comment"
))
...
...
@@ -33,17 +35,28 @@ class Setting(models.Model):
return
self
.
name
@property
def
value_
(
self
):
def
cleaned_value
(
self
):
try
:
return
json
.
loads
(
self
.
value
)
except
json
.
JSONDecodeError
:
return
None
@cleaned_value.setter
def
cleaned_value
(
self
,
item
):
try
:
v
=
json
.
dumps
(
item
)
self
.
value
=
v
except
json
.
JSONDecodeError
as
e
:
raise
ValueError
(
"Json dump error: {}"
.
format
(
str
(
e
)))
@classmethod
def
refresh_all_settings
(
cls
):
try
:
settings_list
=
cls
.
objects
.
all
()
for
setting
in
settings_list
:
setting
.
refresh_setting
()
except
(
ProgrammingError
,
OperationalError
):
pass
def
refresh_setting
(
self
):
try
:
...
...
@@ -53,9 +66,9 @@ class Setting(models.Model):
setattr
(
settings
,
self
.
name
,
value
)
if
self
.
name
==
"AUTH_LDAP"
:
if
self
.
value_
and
settings
.
AUTH_LDAP_BACKEND
not
in
settings
.
AUTHENTICATION_BACKENDS
:
if
self
.
cleaned_value
and
settings
.
AUTH_LDAP_BACKEND
not
in
settings
.
AUTHENTICATION_BACKENDS
:
settings
.
AUTHENTICATION_BACKENDS
.
insert
(
0
,
settings
.
AUTH_LDAP_BACKEND
)
elif
not
self
.
value_
and
settings
.
AUTH_LDAP_BACKEND
in
settings
.
AUTHENTICATION_BACKENDS
:
elif
not
self
.
cleaned_value
and
settings
.
AUTH_LDAP_BACKEND
in
settings
.
AUTHENTICATION_BACKENDS
:
settings
.
AUTHENTICATION_BACKENDS
.
remove
(
settings
.
AUTH_LDAP_BACKEND
)
if
self
.
name
==
"AUTH_LDAP_SEARCH_FILTER"
:
...
...
apps/common/templates/common/basic_setting.html
View file @
f37b3316
...
...
@@ -20,6 +20,9 @@
<li>
<a
href=
"{% url 'settings:ldap-setting' %}"
class=
"text-center"
><i
class=
"fa fa-archive"
></i>
{% trans 'LDAP setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/email_setting.html
View file @
f37b3316
...
...
@@ -20,6 +20,9 @@
<li>
<a
href=
"{% url 'settings:ldap-setting' %}"
class=
"text-center"
><i
class=
"fa fa-archive"
></i>
{% trans 'LDAP setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/ldap_setting.html
View file @
f37b3316
...
...
@@ -20,6 +20,9 @@
<li
class=
"active"
>
<a
href=
"{% url 'settings:ldap-setting' %}"
class=
"text-center"
><i
class=
"fa fa-archive"
></i>
{% trans 'LDAP setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/terminal_setting.html
0 → 100644
View file @
f37b3316
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"panel-options"
>
<ul
class=
"nav nav-tabs"
>
<li>
<a
href=
"{% url 'settings:basic-setting' %}"
class=
"text-center"
><i
class=
"fa fa-cubes"
></i>
{% trans 'Basic setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:email-setting' %}"
class=
"text-center"
><i
class=
"fa fa-envelope"
></i>
{% trans 'Email setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:ldap-setting' %}"
class=
"text-center"
><i
class=
"fa fa-archive"
></i>
{% trans 'LDAP setting' %}
</a>
</li>
<li
class=
"active"
>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
<div
class=
"col-sm-12"
style=
"padding-left:0"
>
<div
class=
"ibox-content"
style=
"border-width: 0;padding-top: 40px;"
>
<form
action=
""
method=
"post"
class=
"form-horizontal"
>
{% if form.non_field_errors %}
<div
class=
"alert alert-danger"
>
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>
{% trans "Basic setting" %}
</h3>
{% for field in form %}
{% if not field.field|is_bool_field %}
{% bootstrap_field field layout="horizontal" %}
{% else %}
<div
class=
"form-group"
>
<label
for=
"{{ field.id_for_label }}"
class=
"col-sm-2 control-label"
>
{{ field.label }}
</label>
<div
class=
"col-sm-8"
>
<div
class=
"col-sm-1"
>
{{ field }}
</div>
<div
class=
"col-sm-9"
>
<span
class=
"help-block"
>
{{ field.help_text }}
</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans "Command storage" %}
</h3>
<table
class=
"table table-hover "
id=
"task-history-list-table"
>
<thead>
<tr>
<th>
{% trans 'Name' %}
</th>
<th>
{% trans 'Type' %}
</th>
</tr>
</thead>
<tbody>
{% for name, setting in command_storage.items %}
<tr>
<td>
{{ name }}
</td>
<td>
{{ setting.TYPE }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{#
<button
class=
"btn btn-default btn-circle btn-add-command-storage"
data-toggle=
"modal"
data-target=
"#add_command_storage_model"
tabindex=
"0"
type=
"button"
><i
class=
"fa fa-plus"
></i></button>
#}
{#
<div
class=
"hr-line-dashed"
></div>
#}
{#
<h3>
{% trans "Replay storage" %}
</h3>
#}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"form-group"
>
<div
class=
"col-sm-4 col-sm-offset-2"
>
<button
class=
"btn btn-default"
type=
"reset"
>
{% trans 'Reset' %}
</button>
<button
id=
"submit_button"
class=
"btn btn-primary"
type=
"submit"
>
{% trans 'Submit' %}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
})
.
on
(
"click"
,
".btn-test"
,
function
()
{
var
data
=
{};
var
form
=
$
(
"form"
).
serializeArray
();
$
.
each
(
form
,
function
(
i
,
field
)
{
data
[
field
.
name
]
=
field
.
value
;
});
var
the_url
=
"{% url 'api-common:ldap-testing' %}"
;
function
error
(
message
)
{
toastr
.
error
(
message
)
}
function
success
(
message
)
{
toastr
.
success
(
message
.
msg
)
}
APIUpdateAttr
({
url
:
the_url
,
body
:
JSON
.
stringify
(
data
),
method
:
"POST"
,
flash_message
:
false
,
success
:
success
,
error
:
error
});
})
.
on
(
'click'
,
''
,
function
()
{
})
</script>
{% endblock %}
apps/common/urls/view_urls.py
View file @
f37b3316
...
...
@@ -10,4 +10,5 @@ urlpatterns = [
url
(
r'^$'
,
views
.
BasicSettingView
.
as_view
(),
name
=
'basic-setting'
),
url
(
r'^email/$'
,
views
.
EmailSettingView
.
as_view
(),
name
=
'email-setting'
),
url
(
r'^ldap/$'
,
views
.
LDAPSettingView
.
as_view
(),
name
=
'ldap-setting'
),
url
(
r'^terminal/$'
,
views
.
TerminalSettingView
.
as_view
(),
name
=
'terminal-setting'
),
]
apps/common/views.py
View file @
f37b3316
from
django.views.generic
import
View
,
TemplateView
from
django.views.generic
import
TemplateView
from
django.shortcuts
import
render
,
redirect
from
django.contrib
import
messages
from
django.utils.translation
import
ugettext
as
_
from
django.conf
import
settings
from
.forms
import
EmailSettingForm
,
LDAPSettingForm
,
BasicSettingForm
from
.forms
import
EmailSettingForm
,
LDAPSettingForm
,
BasicSettingForm
,
\
TerminalSettingForm
from
.models
import
Setting
from
.mixins
import
AdminUserRequiredMixin
from
.signals
import
ldap_auth_enable
...
...
@@ -25,8 +28,6 @@ class BasicSettingView(AdminUserRequiredMixin, TemplateView):
form
=
self
.
form_class
(
request
.
POST
)
if
form
.
is_valid
():
form
.
save
()
if
"AUTH_LDAP"
in
form
.
cleaned_data
:
ldap_auth_enable
.
send
(
form
.
cleaned_data
[
"AUTH_LDAP"
])
msg
=
_
(
"Update setting successfully, please restart program"
)
messages
.
success
(
request
,
msg
)
return
redirect
(
'settings:basic-setting'
)
...
...
@@ -79,6 +80,8 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
form
=
self
.
form_class
(
request
.
POST
)
if
form
.
is_valid
():
form
.
save
()
if
"AUTH_LDAP"
in
form
.
cleaned_data
:
ldap_auth_enable
.
send
(
form
.
cleaned_data
[
"AUTH_LDAP"
])
msg
=
_
(
"Update setting successfully, please restart program"
)
messages
.
success
(
request
,
msg
)
return
redirect
(
'settings:ldap-setting'
)
...
...
@@ -86,3 +89,31 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
context
=
self
.
get_context_data
()
context
.
update
({
"form"
:
form
})
return
render
(
request
,
self
.
template_name
,
context
)
class
TerminalSettingView
(
AdminUserRequiredMixin
,
TemplateView
):
form_class
=
TerminalSettingForm
template_name
=
"common/terminal_setting.html"
def
get_context_data
(
self
,
**
kwargs
):
command_storage
=
settings
.
TERMINAL_COMMAND_STORAGE
context
=
{
'app'
:
_
(
'Settings'
),
'action'
:
_
(
'Terminal setting'
),
'form'
:
self
.
form_class
(),
'command_storage'
:
command_storage
,
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
def
post
(
self
,
request
):
form
=
self
.
form_class
(
request
.
POST
)
if
form
.
is_valid
():
form
.
save
()
msg
=
_
(
"Update setting successfully, please restart program"
)
messages
.
success
(
request
,
msg
)
return
redirect
(
'settings:terminal-setting'
)
else
:
context
=
self
.
get_context_data
()
context
.
update
({
"form"
:
form
})
return
render
(
request
,
self
.
template_name
,
context
)
apps/jumpserver/settings.py
View file @
f37b3316
...
...
@@ -274,7 +274,7 @@ EMAIL_HOST_USER = CONFIG.EMAIL_HOST_USER
EMAIL_HOST_PASSWORD
=
CONFIG
.
EMAIL_HOST_PASSWORD
EMAIL_USE_SSL
=
CONFIG
.
EMAIL_USE_SSL
EMAIL_USE_TLS
=
CONFIG
.
EMAIL_USE_TLS
EMAIL_SUBJECT_PREFIX
=
CONFIG
.
EMAIL_SUBJECT_PREFIX
EMAIL_SUBJECT_PREFIX
=
CONFIG
.
EMAIL_SUBJECT_PREFIX
or
''
REST_FRAMEWORK
=
{
# Use Django's standard `django.contrib.auth` permissions,
...
...
@@ -298,7 +298,7 @@ REST_FRAMEWORK = {
'DATETIME_FORMAT'
:
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S
%
z'
,
'DATETIME_INPUT_FORMATS'
:
[
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S
%
z'
],
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE'
:
15
#
'PAGE_SIZE': 15
}
AUTHENTICATION_BACKENDS
=
[
...
...
@@ -373,7 +373,20 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
CAPTCHA_NOISE_FUNCTIONS
=
(
'captcha.helpers.noise_dots'
,)
CAPTCHA_TEST_MODE
=
CONFIG
.
CAPTCHA_TEST_MODE
COMMAND_STORAGE_BACKEND
=
'terminal.backends.command.db'
COMMAND_STORAGE
=
{
'ENGINE'
:
'terminal.backends.command.db'
,
}
TERMINAL_COMMAND_STORAGE
=
{
"default"
:
{
"TYPE"
:
"server"
,
},
# 'ali-es': {
# 'TYPE': 'elasticsearch',
# 'HOSTS': ['http://elastic:changeme@localhost:9200'],
# },
}
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3
=
{
...
...
apps/locale/zh/LC_MESSAGES/django.mo
View file @
f37b3316
No preview for this file type
apps/locale/zh/LC_MESSAGES/django.po
View file @
f37b3316
This diff is collapsed.
Click to expand it.
apps/ops/models.py
View file @
f37b3316
...
...
@@ -165,7 +165,7 @@ class AdHoc(models.Model):
if
item
and
isinstance
(
item
,
list
):
self
.
_tasks
=
json
.
dumps
(
item
)
else
:
raise
SyntaxError
(
'Tasks should be a list
'
)
raise
SyntaxError
(
'Tasks should be a list
: {}'
.
format
(
item
)
)
@property
def
hosts
(
self
):
...
...
apps/ops/utils.py
View file @
f37b3316
...
...
@@ -16,6 +16,8 @@ def update_or_create_ansible_task(
run_as_admin
=
False
,
run_as
=
""
,
become_info
=
None
,
created_by
=
None
,
):
if
not
hosts
or
not
tasks
or
not
task_name
:
return
defaults
=
{
'name'
:
task_name
,
...
...
@@ -41,7 +43,7 @@ def update_or_create_ansible_task(
new_adhoc
.
become
=
become_info
if
not
adhoc
or
adhoc
!=
new_adhoc
:
logger
.
debug
(
"Task create new adhoc: {}"
.
format
(
task_name
))
print
(
"Task create new adhoc: {}"
.
format
(
task_name
))
new_adhoc
.
save
()
task
.
latest_adhoc
=
new_adhoc
created
=
True
...
...
apps/static/css/jumpserver.css
View file @
f37b3316
...
...
@@ -303,7 +303,7 @@ div.dataTables_wrapper div.dataTables_filter {
.profile-element
div
:first-child
{
line-height
:
60px
;
width
:
70px
;
/*width: 70px;*/
float
:
left
;
text-align
:
center
;
}
...
...
apps/static/img/avatar/admin.png
View replaced file @
5bbad019
View file @
f37b3316
106 KB
|
W:
|
H:
11.5 KB
|
W:
|
H:
2-up
Swipe
Onion skin
apps/static/img/avatar/user.png
View replaced file @
5bbad019
View file @
f37b3316
61.3 KB
|
W:
|
H:
11.5 KB
|
W:
|
H:
2-up
Swipe
Onion skin
apps/static/img/logo-text.png
0 → 100644
View file @
f37b3316
12.4 KB
apps/templates/_footer.html
View file @
f37b3316
...
...
@@ -4,6 +4,6 @@
<img
style=
"display: none"
src=
"http://www.jumpserver.org/img/evaluate_avatar1.jpg"
>
</div>
<div>
<strong>
Copyright
</strong>
北京堆栈科技有限公司
©
2014-201
7
<strong>
Copyright
</strong>
北京堆栈科技有限公司
©
2014-201
8
</div>
</div>
\ No newline at end of file
apps/templates/_header_bar.html
View file @
f37b3316
...
...
@@ -10,9 +10,9 @@
<!--</form>-->
</div>
<ul
class=
"nav navbar-top-links navbar-right"
>
<li>
<span
class=
"m-r-sm text-muted welcome-message"
>
{% trans 'Welcome to use Jumpserver system' %}
</span>
</li>
{#
<li>
#}
{#
<span
class=
"m-r-sm text-muted welcome-message"
>
{% trans 'Welcome to use Jumpserver system' %}
</span>
#}
{#
</li>
#}
<li
class=
"dropdown"
>
<a
class=
"dropdown-toggle count-info"
data-toggle=
"dropdown"
href=
"#"
>
<span
class=
"m-r-sm text-muted welcome-message"
>
{% trans 'Help' %}
</span>
...
...
@@ -22,11 +22,10 @@
{% if request.user.is_authenticated %}
<a
data-toggle=
"dropdown"
class=
"dropdown-toggle"
href=
"#"
>
<span
class=
"m-r-sm text-muted welcome-message"
>
<img
alt=
"image"
class=
"img-circle"
width=
"40"
height=
"40"
src=
"{{ request.user.avatar_url }}"
/>
<strong
class=
"font-bold"
>
{{ request.user.name }}
<span
style=
"color: #8095a8"
></span>
<img
alt=
"image"
class=
"img-circle"
width=
"30"
height=
"30"
src=
"{{ request.user.avatar_url }}"
/>
<span
style=
"font-size: 13px;font-weight: 400"
>
{{ request.user.name }}
<b
class=
"caret"
></b>
</s
trong
>
</s
pan
>
</span>
</a>
<ul
class=
"dropdown-menu animated fadeInRight m-t-xs"
>
...
...
apps/templates/_user_profile.html
View file @
f37b3316
...
...
@@ -2,11 +2,8 @@
{% load i18n %}
<li
class=
"nav-header"
>
<div
class=
"dropdown profile-element"
>
<div>
<img
alt=
"image"
height=
"40"
src=
"/static/img/logo.png"
/>
</div>
<div>
<a
href=
"http://www.jumpserver.org"
target=
"_blank"
>
Jumpserver
</a>
<div
href=
"http://www.jumpserver.org"
target=
"_blank"
>
<img
alt=
"image"
height=
"55"
src=
"/static/img/logo-text.png"
style=
"margin-left: 10px"
/>
</div>
</div>
<div
class=
"clearfix"
></div>
...
...
apps/templates/flash_message_standalone.html
View file @
f37b3316
...
...
@@ -52,7 +52,7 @@
Copyright Jumpserver.org
</div>
<div
class=
"col-md-6 text-right"
>
<small>
2014-201
7
</small>
<small>
2014-201
8
</small>
</div>
</div>
</div>
...
...
apps/templates/index.html
View file @
f37b3316
...
...
@@ -164,7 +164,7 @@
{% for login in last_login_ten %}
<div
class=
"feed-element"
>
<a
href=
"#"
class=
"pull-left"
>
<img
alt=
"image"
class=
"img-circle"
src=
"
/static/img/root.png
"
>
<img
alt=
"image"
class=
"img-circle"
src=
"
{% static 'img/avatar/user.png' %}
"
>
</a>
<div
class=
"media-body "
>
{% ifequal login.is_finished 0 %}
...
...
apps/terminal/api.py
View file @
f37b3316
# -*- coding: utf-8 -*-
#
from
collections
import
OrderedDict
import
copy
import
logging
import
os
import
uuid
...
...
@@ -21,7 +20,8 @@ from .serializers import TerminalSerializer, StatusSerializer, \
SessionSerializer
,
TaskSerializer
,
ReplaySerializer
from
.hands
import
IsSuperUserOrAppUser
,
IsAppUser
,
\
IsSuperUserOrAppUserOrUserReadonly
from
.backends
import
get_command_store
,
SessionCommandSerializer
from
.backends
import
get_command_store
,
get_multi_command_store
,
\
SessionCommandSerializer
logger
=
logging
.
getLogger
(
__file__
)
...
...
@@ -141,7 +141,9 @@ class StatusViewSet(viewsets.ModelViewSet):
session
=
serializer
.
save
()
return
session
else
:
msg
=
"session data is not valid {}"
.
format
(
serializer
.
errors
)
msg
=
"session data is not valid {}: {}"
.
format
(
serializer
.
errors
,
str
(
serializer
.
data
)
)
logger
.
error
(
msg
)
return
None
...
...
@@ -195,6 +197,7 @@ class CommandViewSet(viewsets.ViewSet):
"""
command_store
=
get_command_store
()
multi_command_storage
=
get_multi_command_store
()
serializer_class
=
SessionCommandSerializer
permission_classes
=
(
IsSuperUserOrAppUser
,)
...
...
@@ -215,7 +218,7 @@ class CommandViewSet(viewsets.ViewSet):
return
Response
({
"msg"
:
msg
},
status
=
401
)
def
list
(
self
,
request
,
*
args
,
**
kwargs
):
queryset
=
list
(
self
.
command_store
.
all
()
)
queryset
=
self
.
multi_command_storage
.
filter
(
)
serializer
=
self
.
serializer_class
(
queryset
,
many
=
True
)
return
Response
(
serializer
.
data
)
...
...
@@ -258,3 +261,13 @@ class SessionReplayViewSet(viewsets.ViewSet):
return
redirect
(
url
)
else
:
return
HttpResponseNotFound
()
class
TerminalConfig
(
APIView
):
permission_classes
=
(
IsAppUser
,)
def
get
(
self
,
request
):
user
=
request
.
user
terminal
=
user
.
terminal
configs
=
terminal
.
config
return
Response
(
configs
,
status
=
200
)
apps/terminal/backends/__init__.py
View file @
f37b3316
...
...
@@ -2,9 +2,39 @@ from importlib import import_module
from
django.conf
import
settings
from
.command.serializers
import
SessionCommandSerializer
TYPE_ENGINE_MAPPING
=
{
'elasticsearch'
:
'terminal.backends.command.es'
,
}
def
get_command_store
():
command_engine
=
import_module
(
settings
.
COMMAND_STORAGE_BACKEND
)
command_store
=
command_engine
.
CommandStore
()
return
command_store
params
=
settings
.
COMMAND_STORAGE
engine_class
=
import_module
(
params
[
'ENGINE'
])
storage
=
engine_class
.
CommandStore
(
params
)
return
storage
def
get_terminal_command_store
():
storage_list
=
{}
for
name
,
params
in
settings
.
TERMINAL_COMMAND_STORAGE
.
items
():
tp
=
params
[
'TYPE'
]
if
tp
==
'server'
:
storage
=
get_command_store
()
else
:
if
not
TYPE_ENGINE_MAPPING
.
get
(
tp
):
raise
AssertionError
(
"Command storage type should in {}"
.
format
(
', '
.
join
(
TYPE_ENGINE_MAPPING
.
keys
()))
)
engine_class
=
import_module
(
TYPE_ENGINE_MAPPING
[
tp
])
storage
=
engine_class
.
CommandStore
(
params
)
storage_list
[
name
]
=
storage
return
storage_list
def
get_multi_command_store
():
from
.command.multi
import
CommandStore
storage_list
=
get_terminal_command_store
()
.
values
()
storage
=
CommandStore
(
storage_list
)
return
storage
apps/terminal/backends/command/base.py
View file @
f37b3316
...
...
@@ -19,3 +19,9 @@ class CommandBase(object):
input
=
None
,
session
=
None
):
pass
@abc.abstractmethod
def
count
(
self
,
date_from
=
None
,
date_to
=
None
,
user
=
None
,
asset
=
None
,
system_user
=
None
,
input
=
None
,
session
=
None
):
pass
apps/terminal/backends/command/db.py
View file @
f37b3316
...
...
@@ -8,7 +8,7 @@ from .base import CommandBase
class
CommandStore
(
CommandBase
):
def
__init__
(
self
):
def
__init__
(
self
,
params
):
from
terminal.models
import
Command
self
.
model
=
Command
...
...
@@ -37,7 +37,9 @@ class CommandStore(CommandBase):
))
return
self
.
model
.
objects
.
bulk_create
(
_commands
)
def
filter
(
self
,
date_from
=
None
,
date_to
=
None
,
@staticmethod
def
make_filter_kwargs
(
date_from
=
None
,
date_to
=
None
,
user
=
None
,
asset
=
None
,
system_user
=
None
,
input
=
None
,
session
=
None
):
filter_kwargs
=
{}
...
...
@@ -60,10 +62,28 @@ class CommandStore(CommandBase):
if
session
:
filter_kwargs
[
'session'
]
=
session
return
filter_kwargs
def
filter
(
self
,
date_from
=
None
,
date_to
=
None
,
user
=
None
,
asset
=
None
,
system_user
=
None
,
input
=
None
,
session
=
None
):
filter_kwargs
=
self
.
make_filter_kwargs
(
date_from
=
date_from
,
date_to
=
date_to
,
user
=
user
,
asset
=
asset
,
system_user
=
system_user
,
input
=
input
,
session
=
session
,
)
queryset
=
self
.
model
.
objects
.
filter
(
**
filter_kwargs
)
return
queryset
return
[
command
.
to_dict
()
for
command
in
queryset
]
def
count
(
self
,
date_from
=
None
,
date_to
=
None
,
user
=
None
,
asset
=
None
,
system_user
=
None
,
input
=
None
,
session
=
None
):
filter_kwargs
=
self
.
make_filter_kwargs
(
date_from
=
date_from
,
date_to
=
date_to
,
user
=
user
,
asset
=
asset
,
system_user
=
system_user
,
input
=
input
,
session
=
session
,
)
count
=
self
.
model
.
objects
.
filter
(
**
filter_kwargs
)
.
count
()
return
count
def
all
(
self
):
"""返回所有数据"""
return
self
.
model
.
objects
.
iterator
()
apps/terminal/backends/command/es.py
0 → 100644
View file @
f37b3316
# -*- coding: utf-8 -*-
#
from
jms_es_sdk
import
ESStore
from
.base
import
CommandBase
class
CommandStore
(
CommandBase
,
ESStore
):
def
__init__
(
self
,
params
):
hosts
=
params
.
get
(
'HOSTS'
,
[
'http://localhost'
])
ESStore
.
__init__
(
self
,
hosts
=
hosts
)
def
save
(
self
,
command
):
return
ESStore
.
save
(
self
,
command
)
def
bulk_save
(
self
,
commands
):
return
ESStore
.
bulk_save
(
self
,
commands
)
def
filter
(
self
,
date_from
=
None
,
date_to
=
None
,
user
=
None
,
asset
=
None
,
system_user
=
None
,
input
=
None
,
session
=
None
):
data
=
ESStore
.
filter
(
self
,
date_from
=
date_from
,
date_to
=
date_to
,
user
=
user
,
asset
=
asset
,
system_user
=
system_user
,
input
=
input
,
session
=
session
)
return
[
item
[
"_source"
]
for
item
in
data
[
"hits"
]
if
item
]
def
count
(
self
,
date_from
=
None
,
date_to
=
None
,
user
=
None
,
asset
=
None
,
system_user
=
None
,
input
=
None
,
session
=
None
):
amount
=
ESStore
.
count
(
self
,
date_from
=
date_from
,
date_to
=
date_to
,
user
=
user
,
asset
=
asset
,
system_user
=
system_user
,
input
=
input
,
session
=
session
)
return
amount
apps/terminal/backends/command/models.py
View file @
f37b3316
...
...
@@ -18,5 +18,26 @@ class AbstractSessionCommand(models.Model):
class
Meta
:
abstract
=
True
@classmethod
def
from_dict
(
cls
,
d
):
self
=
cls
()
for
k
,
v
in
d
.
items
():
setattr
(
self
,
k
,
v
)
return
self
@classmethod
def
from_multi_dict
(
cls
,
l
):
commands
=
[]
for
d
in
l
:
command
=
cls
.
from_dict
(
d
)
commands
.
append
(
command
)
return
commands
def
to_dict
(
self
):
d
=
{}
for
field
in
self
.
_meta
.
fields
:
d
[
field
.
name
]
=
getattr
(
self
,
field
.
name
)
return
d
def
__str__
(
self
):
return
self
.
input
apps/terminal/backends/command/multi.py
0 → 100644
View file @
f37b3316
# -*- coding: utf-8 -*-
#
from
.base
import
CommandBase
class
CommandStore
(
CommandBase
):
def
__init__
(
self
,
storage_list
):
self
.
storage_list
=
storage_list
def
filter
(
self
,
**
kwargs
):
queryset
=
[]
for
storage
in
self
.
storage_list
:
queryset
.
extend
(
storage
.
filter
(
**
kwargs
))
return
sorted
(
queryset
,
key
=
lambda
command
:
command
[
"timestamp"
],
reverse
=
True
)
def
count
(
self
,
**
kwargs
):
amount
=
0
for
storage
in
self
.
storage_list
:
amount
+=
storage
.
count
(
**
kwargs
)
return
amount
def
save
(
self
,
command
):
pass
def
bulk_save
(
self
,
commands
):
pass
apps/terminal/forms.py
View file @
f37b3316
...
...
@@ -2,15 +2,27 @@
#
from
django
import
forms
from
django.conf
import
settings
from
django.utils.translation
import
ugettext_lazy
as
_
from
.models
import
Terminal
def
get_all_command_storage
():
# storage_choices = []
from
common.models
import
Setting
Setting
.
refresh_all_settings
()
for
k
,
v
in
settings
.
TERMINAL_COMMAND_STORAGE
.
items
():
yield
(
k
,
k
)
class
TerminalForm
(
forms
.
ModelForm
):
command_storage
=
forms
.
ChoiceField
(
choices
=
get_all_command_storage
(),
label
=
_
(
"Command storage"
))
class
Meta
:
model
=
Terminal
fields
=
[
'name'
,
'remote_addr'
,
'ssh_port'
,
'http_port'
,
'comment'
]
fields
=
[
'name'
,
'remote_addr'
,
'ssh_port'
,
'http_port'
,
'comment'
,
'command_storage'
]
help_texts
=
{
'ssh_port'
:
_
(
"Coco ssh listen port"
),
'http_port'
:
_
(
"Coco http/ws listen port"
),
...
...
apps/terminal/models.py
View file @
f37b3316
...
...
@@ -4,6 +4,7 @@ import uuid
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.conf
import
settings
from
users.models
import
User
from
.backends.command.models
import
AbstractSessionCommand
...
...
@@ -15,6 +16,8 @@ class Terminal(models.Model):
remote_addr
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
'Remote Address'
))
ssh_port
=
models
.
IntegerField
(
verbose_name
=
_
(
'SSH Port'
),
default
=
2222
)
http_port
=
models
.
IntegerField
(
verbose_name
=
_
(
'HTTP Port'
),
default
=
5000
)
command_storage
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Command storage"
),
default
=
'default'
)
replay_storage
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Replay storage"
),
default
=
'default'
)
user
=
models
.
OneToOneField
(
User
,
related_name
=
'terminal'
,
verbose_name
=
'Application User'
,
null
=
True
,
on_delete
=
models
.
CASCADE
)
is_accepted
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'Is Accepted'
)
is_deleted
=
models
.
BooleanField
(
default
=
False
)
...
...
@@ -33,6 +36,26 @@ class Terminal(models.Model):
self
.
user
.
is_active
=
active
self
.
user
.
save
()
def
get_common_storage
(
self
):
storage_all
=
settings
.
TERMINAL_COMMAND_STORAGE
if
self
.
command_storage
in
storage_all
:
storage
=
storage_all
.
get
(
self
.
command_storage
)
else
:
storage
=
storage_all
.
get
(
'default'
)
return
{
"TERMINAL_COMMAND_STORAGE"
:
storage
}
def
get_replay_storage
(
self
):
pass
@property
def
config
(
self
):
configs
=
{}
for
k
in
dir
(
settings
):
if
k
.
startswith
(
'TERMINAL'
):
configs
[
k
]
=
getattr
(
settings
,
k
)
configs
.
update
(
self
.
get_common_storage
())
return
configs
def
create_app_user
(
self
):
random
=
uuid
.
uuid4
()
.
hex
[:
6
]
user
,
access_key
=
User
.
create_app_user
(
name
=
"{}-{}"
.
format
(
self
.
name
,
random
),
comment
=
self
.
comment
)
...
...
apps/terminal/serializers.py
View file @
f37b3316
...
...
@@ -5,7 +5,7 @@ from django.utils import timezone
from
rest_framework
import
serializers
from
.models
import
Terminal
,
Status
,
Session
,
Task
from
.backends
import
get_command_store
from
.backends
import
get_
multi_
command_store
class
TerminalSerializer
(
serializers
.
ModelSerializer
):
...
...
@@ -43,14 +43,14 @@ class TerminalSerializer(serializers.ModelSerializer):
class
SessionSerializer
(
serializers
.
ModelSerializer
):
command_amount
=
serializers
.
SerializerMethodField
()
command_store
=
get_command_store
()
command_store
=
get_
multi_
command_store
()
class
Meta
:
model
=
Session
fields
=
'__all__'
def
get_command_amount
(
self
,
obj
):
return
len
(
self
.
command_store
.
filter
(
session
=
obj
.
session
))
return
self
.
command_store
.
count
(
session
=
str
(
obj
.
id
))
class
StatusSerializer
(
serializers
.
ModelSerializer
):
...
...
apps/terminal/signals_handler.py
View file @
f37b3316
...
...
@@ -13,10 +13,6 @@ RUNNING = False
logger
=
get_logger
(
__file__
)
@shared_task
@register_as_period_task
(
interval
=
3600
)
@after_app_ready_start
@after_app_shutdown_clean
def
set_session_info_cache
():
logger
.
debug
(
""
)
from
.utils
import
get_session_asset_list
,
get_session_user_list
,
\
...
...
apps/terminal/templates/terminal/terminal_modal_accept.html
View file @
f37b3316
...
...
@@ -12,6 +12,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %}
{% bootstrap_field form.command_storage layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
</form>
...
...
apps/terminal/templates/terminal/terminal_update.html
View file @
f37b3316
...
...
@@ -35,6 +35,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %}
{% bootstrap_field form.command_storage layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Other' %}
</h3>
...
...
apps/terminal/templatetags/terminal_tags.py
View file @
f37b3316
# ~*~ coding: utf-8 ~*~
from
django
import
template
from
..backends
import
get_command_store
from
..backends
import
get_
multi_
command_store
register
=
template
.
Library
()
command_store
=
get_command_store
()
command_store
=
get_
multi_
command_store
()
@register.filter
def
get_session_command_amount
(
session_id
):
return
len
(
command_store
.
filter
(
session
=
str
(
session_id
)))
return
command_store
.
count
(
session
=
session_id
)
apps/terminal/urls/api_urls.py
View file @
f37b3316
...
...
@@ -20,7 +20,8 @@ urlpatterns = [
url
(
r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$'
,
api
.
SessionReplayViewSet
.
as_view
({
'get'
:
'retrieve'
,
'post'
:
'create'
}),
name
=
'session-replay'
),
url
(
r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key'
,
api
.
TerminalTokenApi
.
as_view
(),
name
=
'terminal-access-key'
)
url
(
r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key'
,
api
.
TerminalTokenApi
.
as_view
(),
name
=
'terminal-access-key'
),
url
(
r'^v1/terminal/config'
,
api
.
TerminalConfig
.
as_view
(),
name
=
'terminal-config'
),
]
urlpatterns
+=
router
.
urls
apps/terminal/views/command.py
View file @
f37b3316
...
...
@@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _
from
common.mixins
import
DatetimeSearchMixin
from
..models
import
Command
from
..
import
utils
from
..backends
import
get_command_store
from
..backends
import
get_
multi_
command_store
__all__
=
[
'CommandListView'
]
comm
and_store
=
get
_command_store
()
comm
on_storage
=
get_multi
_command_store
()
class
CommandListView
(
DatetimeSearchMixin
,
ListView
):
...
...
@@ -39,7 +39,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
filter_kwargs
[
'system_user'
]
=
self
.
system_user
if
self
.
command
:
filter_kwargs
[
'input'
]
=
self
.
command
queryset
=
comm
and_stor
e
.
filter
(
**
filter_kwargs
)
queryset
=
comm
on_storag
e
.
filter
(
**
filter_kwargs
)
return
queryset
def
get_context_data
(
self
,
**
kwargs
):
...
...
apps/terminal/views/session.py
View file @
f37b3316
...
...
@@ -10,7 +10,7 @@ from django.conf import settings
from
users.utils
import
AdminUserRequiredMixin
from
common.mixins
import
DatetimeSearchMixin
from
..models
import
Session
,
Command
,
Terminal
from
..backends
import
get_command_store
from
..backends
import
get_
multi_
command_store
from
..
import
utils
...
...
@@ -19,7 +19,7 @@ __all__ = [
'SessionDetailView'
,
]
command_store
=
get_command_store
()
command_store
=
get_
multi_
command_store
()
class
SessionListView
(
AdminUserRequiredMixin
,
DatetimeSearchMixin
,
ListView
):
...
...
apps/users/api.py
View file @
f37b3316
# ~*~ coding: utf-8 ~*~
from
rest_framework
import
generics
from
rest_framework.permissions
import
AllowAny
from
rest_framework.permissions
import
AllowAny
,
IsAuthenticated
from
rest_framework.response
import
Response
from
rest_framework.views
import
APIView
from
rest_framework_bulk
import
BulkModelViewSet
from
.serializers
import
UserSerializer
,
UserGroupSerializer
,
\
UserGroupUpdateMemeberSerializer
,
UserPKUpdateSerializer
,
\
UserUpdateGroupSerializer
UserUpdateGroupSerializer
,
ChangeUserPasswordSerializer
from
.tasks
import
write_login_log_async
from
.models
import
User
,
UserGroup
from
.permissions
import
IsSuperUser
,
IsValidUser
,
IsCurrentUserOrReadOnly
...
...
@@ -24,10 +24,21 @@ class UserViewSet(CustomFilterMixin, BulkModelViewSet):
queryset
=
User
.
objects
.
exclude
(
role
=
"App"
)
# queryset = User.objects.all().exclude(role="App").order_by("date_joined")
serializer_class
=
UserSerializer
permission_classes
=
(
IsSuperUser
,)
permission_classes
=
(
IsSuperUser
,
IsAuthenticated
)
filter_fields
=
(
'username'
,
'email'
,
'name'
,
'id'
)
class
ChangeUserPasswordApi
(
generics
.
RetrieveUpdateAPIView
):
permission_classes
=
(
IsSuperUser
,)
queryset
=
User
.
objects
.
all
()
serializer_class
=
ChangeUserPasswordSerializer
def
perform_update
(
self
,
serializer
):
user
=
self
.
get_object
()
user
.
password_raw
=
serializer
.
validated_data
[
"password"
]
user
.
save
()
class
UserUpdateGroupApi
(
generics
.
RetrieveUpdateAPIView
):
queryset
=
User
.
objects
.
all
()
serializer_class
=
UserUpdateGroupSerializer
...
...
@@ -37,6 +48,7 @@ class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
class
UserResetPasswordApi
(
generics
.
UpdateAPIView
):
queryset
=
User
.
objects
.
all
()
serializer_class
=
UserSerializer
permission_classes
=
(
IsAuthenticated
,)
def
perform_update
(
self
,
serializer
):
# Note: we are not updating the user object here.
...
...
@@ -128,7 +140,11 @@ class UserAuthApi(APIView):
user_agent
=
request
.
data
.
get
(
'HTTP_USER_AGENT'
,
''
)
if
not
login_ip
:
login_ip
=
request
.
META
.
get
(
'HTTP_X_FORWARDED_FOR'
)
or
request
.
META
.
get
(
"REMOTE_ADDR"
)
x_forwarded_for
=
request
.
META
.
get
(
'HTTP_X_FORWARDED_FOR'
,
''
)
.
split
(
','
)
if
x_forwarded_for
:
login_ip
=
x_forwarded_for
[
0
]
else
:
login_ip
=
request
.
META
.
get
(
"REMOTE_ADDR"
)
user
,
msg
=
check_user_valid
(
username
=
username
,
password
=
password
,
...
...
apps/users/forms.py
View file @
f37b3316
...
...
@@ -172,7 +172,6 @@ class UserBulkUpdateForm(forms.ModelForm):
if
self
.
data
.
get
(
field
)
is
not
None
:
changed_fields
.
append
(
field
)
print
(
changed_fields
)
cleaned_data
=
{
k
:
v
for
k
,
v
in
self
.
cleaned_data
.
items
()
if
k
in
changed_fields
}
users
=
cleaned_data
.
pop
(
'users'
,
''
)
...
...
apps/users/hands.py
View file @
f37b3316
...
...
@@ -6,7 +6,7 @@
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-201
7
by Jumpserver Team.
:copyright: (c) 2014-201
8
by Jumpserver Team.
:license: GPL v2, see LICENSE for more details.
"""
...
...
apps/users/serializers.py
View file @
f37b3316
...
...
@@ -71,3 +71,9 @@ class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer):
model
=
UserGroup
fields
=
[
'id'
,
'users'
]
class
ChangeUserPasswordSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
User
fields
=
[
'password'
]
apps/users/templates/users/_user.html
View file @
f37b3316
...
...
@@ -11,8 +11,8 @@
<form
method=
"post"
class=
"form-horizontal"
action=
""
enctype=
"multipart/form-data"
>
{% csrf_token %}
<h3>
{% trans 'Account' %}
</h3>
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.email layout="horizontal" %}
{% bootstrap_field form.groups layout="horizontal" %}
...
...
@@ -32,12 +32,6 @@
<span
class=
"help-block "
>
{{ form.date_expired.errors }}
</span>
</div>
</div>
{#
<div
class=
"form-group"
>
#}
{#
<label
for=
"{{ form.enable_otp.id_for_label }}"
class=
"col-sm-2 control-label"
>
{% trans 'Enable OTP' %}
</label>
#}
{#
<div
class=
"col-sm-8"
>
#}
{# {{ form.enable_otp }}#}
{#
</div>
#}
{#
</div>
#}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Profile' %}
</h3>
{% bootstrap_field form.phone layout="horizontal" %}
...
...
apps/users/templates/users/forgot_password.html
View file @
f37b3316
...
...
@@ -55,7 +55,7 @@
Copyright Jumpserver.org
</div>
<div
class=
"col-md-6 text-right"
>
<small>
© 2014-201
7
</small>
<small>
© 2014-201
8
</small>
</div>
</div>
</div>
...
...
apps/users/templates/users/login.html
View file @
f37b3316
...
...
@@ -78,7 +78,7 @@
Copyright Jumpserver.org
</div>
<div
class=
"col-md-6 text-right"
>
<small>
© 2014-201
7
</small>
<small>
© 2014-201
8
</small>
</div>
</div>
</div>
...
...
apps/users/templates/users/reset_password.html
View file @
f37b3316
...
...
@@ -74,7 +74,7 @@
Copyright Jumpserver.org
</div>
<div
class=
"col-md-6 text-right"
>
<small>
© 2014-201
7
</small>
<small>
© 2014-201
8
</small>
</div>
</div>
</div>
...
...
apps/users/templates/users/user_detail.html
View file @
f37b3316
...
...
@@ -24,8 +24,9 @@
<li
class=
"pull-right"
>
<a
class=
"btn btn-outline btn-default"
href=
"{% url 'users:user-update' pk=user_object.id %}"
><i
class=
"fa fa-edit"
></i>
{% trans 'Update' %}
</a>
</li>
<li
class=
"pull-right"
>
<a
class=
"btn btn-outline
btn-danger btn-delete-user
"
>
<a
class=
"btn btn-outline
{% if request.user != user_object and user_object.username != "
admin
"
%}
btn-danger
btn-delete-user
{%
else
%}
disabled
{%
endif
%}
"
>
<i
class=
"fa fa-trash-o"
></i>
{% trans 'Delete' %}
</a>
</li>
...
...
@@ -128,7 +129,7 @@
<td><span
class=
"pull-right"
>
<div
class=
"switch"
>
<div
class=
"onoffswitch"
>
<input
type=
"checkbox"
{%
if
user_object
.
is_active
%}
checked
{%
endif
%}
class=
"onoffswitch-checkbox"
id=
"is_active"
>
<input
type=
"checkbox"
{%
if
user_object
.
is_active
%}
checked
{%
endif
%}
{%
if
request
.
user =
=
user_object
%}
disabled
{%
endif
%}
class=
"onoffswitch-checkbox"
id=
"is_active"
>
<label
class=
"onoffswitch-label"
for=
"is_active"
>
<span
class=
"onoffswitch-inner"
></span>
<span
class=
"onoffswitch-switch"
></span>
...
...
@@ -156,7 +157,7 @@
<td>
{% trans 'Send reset password mail' %}:
</td>
<td>
<span
class=
"pull-right"
>
<button
type=
"button"
class=
"btn btn-primary btn-xs"
id=
"btn-reset-password"
style=
"width: 54px"
>
{% trans 'Send' %}
</button>
<button
type=
"button"
class=
"btn btn-primary btn-xs"
{%
if
request
.
user =
=
user_object
%}
disabled=
"disabled"
{%
endif
%}
id=
"btn-reset-password"
style=
"width: 54px"
>
{% trans 'Send' %}
</button>
</span>
</td>
</tr>
...
...
@@ -164,7 +165,7 @@
<td>
{% trans 'Send reset ssh key mail' %}:
</td>
<td>
<span
class=
"pull-right"
>
<button
type=
"button"
class=
"btn btn-primary btn-xs"
id=
"btn-reset-pk"
style=
"width: 54px;"
>
{% trans 'Send' %}
</button>
<button
type=
"button"
class=
"btn btn-primary btn-xs"
{%
if
request
.
user =
=
user_object
%}
disabled=
"disabled"
{%
endif
%}
id=
"btn-reset-pk"
style=
"width: 54px;"
>
{% trans 'Send' %}
</button>
</span>
</td>
</tr>
...
...
@@ -331,7 +332,7 @@ $(document).ready(function() {
}
swal({
title: "{% trans '
Are
you
sure
?
' %}",
text: "{% trans "This will reset the user
'
s
password
.
A
password
-
reset
email
will
be
sent
to
the
user
\
's mailbox."
%}",
text: "{% trans "This will reset the user
password and send a reset mail"
%}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
...
...
@@ -356,7 +357,7 @@ $(document).ready(function() {
}
swal({
title: "{% trans '
Are
you
sure
?
' %}",
text: "{% trans '
This
will
reset
the
user
\
's public key.
'
%
}
",
text: "{% trans '
This
will
reset
the
user
public
key
and
send
a
reset
mail
' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
...
...
apps/users/templates/users/user_list.html
View file @
f37b3316
...
...
@@ -76,7 +76,7 @@ function initTable() {
var
update_btn
=
'<a href="{% url "users:user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'00000000-0000-0000-0000-000000000000'
,
cellData
);
var
del_btn
=
""
;
if
(
rowData
.
id
===
1
||
rowData
.
username
===
"admin"
||
rowData
.
username
===
"{{ user.username }}"
)
{
if
(
rowData
.
id
===
1
||
rowData
.
username
===
"admin"
||
rowData
.
username
===
"{{
request.
user.username }}"
)
{
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs" disabled>{% trans "Delete" %}</a>'
.
replace
(
'{{ DEFAULT_PK }}'
,
cellData
)
.
replace
(
'99991938'
,
rowData
.
name
);
...
...
apps/users/urls/api_urls.py
View file @
f37b3316
...
...
@@ -19,6 +19,8 @@ urlpatterns = [
url
(
r'^v1/token/$'
,
api
.
UserToken
.
as_view
(),
name
=
'user-token'
),
url
(
r'^v1/profile/$'
,
api
.
UserProfile
.
as_view
(),
name
=
'user-profile'
),
url
(
r'^v1/auth/$'
,
api
.
UserAuthApi
.
as_view
(),
name
=
'user-auth'
),
url
(
r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/$'
,
api
.
ChangeUserPasswordApi
.
as_view
(),
name
=
'change-user-password'
),
url
(
r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/password/reset/$'
,
api
.
UserResetPasswordApi
.
as_view
(),
name
=
'user-reset-password'
),
url
(
r'^v1/users/(?P<pk>[0-9a-zA-Z\-]{36})/pubkey/reset/$'
,
...
...
apps/users/utils.py
View file @
f37b3316
...
...
@@ -180,7 +180,9 @@ def validate_ip(ip):
def
write_login_log
(
username
,
type
=
''
,
ip
=
''
,
user_agent
=
''
):
if
not
(
ip
and
validate_ip
(
ip
)):
ip
=
'0.0.0.0'
ip
=
ip
[:
15
]
city
=
"Unknown"
else
:
city
=
get_ip_city
(
ip
)
LoginLog
.
objects
.
create
(
username
=
username
,
type
=
type
,
...
...
apps/users/views/login.py
View file @
f37b3316
...
...
@@ -53,8 +53,11 @@ class UserLoginView(FormView):
if
not
self
.
request
.
session
.
test_cookie_worked
():
return
HttpResponse
(
_
(
"Please enable cookies and try again."
))
auth_login
(
self
.
request
,
form
.
get_user
())
login_ip
=
self
.
request
.
META
.
get
(
'HTTP_X_FORWARDED_FOR'
)
or
\
self
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
''
)
x_forwarded_for
=
self
.
request
.
META
.
get
(
'HTTP_X_FORWARDED_FOR'
,
''
)
.
split
(
','
)
if
x_forwarded_for
and
x_forwarded_for
[
0
]:
login_ip
=
x_forwarded_for
[
0
]
else
:
login_ip
=
self
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
''
)
user_agent
=
self
.
request
.
META
.
get
(
'HTTP_USER_AGENT'
,
''
)
write_login_log_async
.
delay
(
self
.
request
.
user
.
username
,
type
=
'W'
,
...
...
docs/install.md
View file @
f37b3316
## Jumpserver v0.4.0 版本安装详细过程
### 环境
-
系统: CentOS 6.5 x86
\_
64 mini
-
Python: 版本 3.6 大部分功能兼容 2.7
-
安装目录
-
/opt/jumpserver
-
/opt/coco
#### 一. 环境准备
##### 1.1 安装基本工具和库
$ yum -y install sqlite-devel git epel-release
$ yum -y install sshpass python-devel libffi-devel openssl-devel
$ yum -y install gcc gcc-c++
##### 1.2 安装Python 3.6 和 虚拟环境
略
#### 二. Jumpserver安装
##### 2.1 下载仓库代码
$ cd /opt
$ git clone https://github.com/jumpserver/jumpserver.git
$ cd jumpserver
$ git checkout dev
##### 2.2 安装依赖
$ cd requirements
$ sudo yum -y install `cat rpm_requirements.txt`
$ pip install -r requirements.txt -i https://pypi.doubanio.com/simple
// 解决Mac安装ldap提示 Modules/LDAPObject.c:18:10: fatal error: 'sasl.h' file not found
pip install python-ldap \
--global-option=build_ext \
--global-option="-I$(xcrun --show-sdk-path)/usr/include/sasl"
##### 2.3 准备配置文件
$ cd ..
$ cp config_example.py config.py
$ vim config.py
// 默认使用的是 DevelpmentConfig 所以应该去修改这部分
class DevelopmentConfig(Config):
EMAIL_HOST = 'smtp.exmail.qq.com'
EMAIL_PORT = 465
EMAIL_HOST_USER = 'ask@jumpserver.org'
EMAIL_HOST_PASSWORD = 'xxx'
EMAIL_USE_SSL = True // 端口是 465 设置 True 否则 False
EMAIL_USE_TLS = False // 端口是 587 设置为 True 否则 False
SITE_URL = 'http://localhost:8080' // 发送邮件会使用这个地址
##### 2.4 初始化数据库
$ cd utils
$ sh make_migrations.sh
$ sh init_db.sh
##### 2.5 安装redis server
$ yum -y install redis
$ service redis start
**2.6 启动**
```
$ cd ..
$ python run_server.py
```
访问 http://ip:8080
账号密码: admin admin
**2.7 测试使用**
-
创建用户
会发送邮件,测试是否正常修改密码,登录
-
创建管理用户
创建一个管理用户, 创建资产时需要关联
-
创建资产
创建一个 资产,关联刚创建的管理用户
-
创建系统用户
系统用户是用来登录资产的,授权时需要
-
创建授权规则
关联用户,资产,系统用户 形成授权规则,授权的系统用户会自动推送到资产上
#### 三. 安装 SSH SERVER - COCO
**3.1 下载代码库**
```
$ cd /opt
$ git clone https://github.com/jumpserver/coco.git
```
**3.2 安装依赖**
```
$ cd coco
$ pip install -r requirements.txt # -i https://pypi.doubanio.com/simple
```
**3.3 启动**
```
$ python run_server.py
```
说明: Coco启动后会向jumpserver注册,请去 jumpserver页面 - 应用程序 - terminal - coco - Accept 允许, 这时 coco就 运行在 2222端口,可以ssh来连接
命令行:
```
ssh admin@YourServerIP -p2222
```
**3.5 测试**
-
测试登录 ssh server
-
测试跳转
-
测试命令记录回
[
1
]:
https://segmentfault.com/a/1190000000654227
[
2
]:
https://github.com/jumpserver/jumpserver.git
[
3
]:
https://github.com/jumpserver/coco.git
More see
[
安装文档
](
https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7
)
requirements/requirements.txt
View file @
f37b3316
...
...
@@ -56,6 +56,8 @@ uritemplate==3.0.0
urllib3==1.22
vine==1.1.4
gunicorn==19.7.1
django_celery_beat==1.1.0
https://github.com/celery/django-celery-beat/zipball/master#egg=django-celery-beat
#django_celery_beat==1.1.0
ephem==3.7.6.0
python-gssapi==0.6.4
jms-es-sdk
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment