Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
PUBLIC
/
surfer-okd
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit c2c00fca
authored
Aug 07, 2019
by
Johannes Zellner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add access token ui and rest api
1 parent
d5940df0
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
105 additions
and
4 deletions
frontend/css/style.css
frontend/index.html
frontend/js/app.js
server.js
src/auth.js
frontend/css/style.css
View file @
c2c00fc
...
...
@@ -89,3 +89,12 @@ a:hover, a:focus {
align-items
:
center
;
justify-content
:
center
;
}
.access-token-input
{
padding
:
5px
0
;
width
:
450px
;
}
.access-token-input
>
input
,
.access-token-input
i
{
cursor
:
copy
!important
;
}
frontend/index.html
View file @
c2c00fc
...
...
@@ -36,6 +36,21 @@
</span>
</el-dialog>
<el-dialog
title=
"Access Tokens"
:visible
.
sync=
"accessTokensDialogVisible"
width=
"30%"
>
Tokens can be used with the surfer
<a
href=
"https://www.npmjs.com/package/cloudron-surfer"
target=
"_blank"
>
cli tool
</a>
or using the Api directly.
They are shared between all users.
<br/>
<br/>
<div>
<div
v-for=
"accessToken in accessTokens"
>
<el-input
suffix-icon=
"el-icon-copy-document"
v-model=
"accessToken"
class=
"access-token-input"
@
focus=
"onCopyAccessToken"
size=
"small"
></el-input>
<el-button
icon=
"el-icon-delete"
type=
"danger"
size=
"small"
@
click=
"onDeleteAccessToken(accessToken)"
></el-button>
</div>
</div>
<br/>
<el-button
@
click=
"onCreateAccessToken()"
size=
"small"
type=
"primary"
>
Create Access Token
</el-button>
</el-dialog>
<el-header>
<el-row
type=
"flex"
justify=
"space-between"
>
<div
style=
"padding: 7px;"
>
...
...
@@ -66,6 +81,7 @@
</el-dropdown-item>
<el-dropdown-item
disabled
divided
>
WebDAV Endpoint
</el-dropdown-item>
<el-dropdown-item><a
href=
"/_webdav/"
target=
"_blank"
>
{{ origin }}/_webdav/
</a></el-dropdown-item>
<el-dropdown-item
command=
"apiAccess"
divided
><i
class=
"el-icon-connection"
></i>
Access Tokens
</el-dropdown-item>
<el-dropdown-item
command=
"about"
divided
><i
class=
"el-icon-info"
></i>
About
</el-dropdown-item>
<el-dropdown-item
command=
"logout"
id=
"logoutButton"
><i
class=
"el-icon-circle-close"
></i>
Logout
</el-dropdown-item>
</el-dropdown-menu>
...
...
frontend/js/app.js
View file @
c2c00fc
...
...
@@ -43,6 +43,8 @@ function initWithToken(accessToken) {
app
.
folderListingEnabled
=
!!
result
.
body
.
folderListingEnabled
;
loadDirectory
(
decode
(
window
.
location
.
hash
.
slice
(
1
)));
app
.
refreshAccessTokens
();
});
});
}
...
...
@@ -278,7 +280,9 @@ var app = new Vue({
password
:
''
,
busy
:
false
},
entries
:
[]
entries
:
[],
accessTokens
:
[],
accessTokensDialogVisible
:
false
},
methods
:
{
onLogin
:
function
()
{
...
...
@@ -312,6 +316,8 @@ var app = new Vue({
}).
then
(
function
()
{}).
catch
(
function
()
{});
}
else
if
(
command
===
'logout'
)
{
logout
();
}
else
if
(
command
===
'apiAccess'
)
{
this
.
accessTokensDialogVisible
=
true
;
}
},
onDownload
:
function
(
entry
)
{
...
...
@@ -415,6 +421,42 @@ var app = new Vue({
});
}).
catch
(
function
()
{});
},
refreshAccessTokens
:
function
()
{
var
that
=
this
;
superagent
.
get
(
'/api/tokens'
).
query
({
access_token
:
localStorage
.
accessToken
}).
end
(
function
(
error
,
result
)
{
if
(
error
&&
!
result
)
return
that
.
$message
.
error
(
error
.
message
);
that
.
accessTokens
=
result
.
body
.
accessTokens
;
});
},
onCopyAccessToken
:
function
(
event
)
{
event
.
target
.
select
();
document
.
execCommand
(
'copy'
);
this
.
$message
({
type
:
'success'
,
message
:
'Access token copied to clipboard'
});
},
onCreateAccessToken
:
function
()
{
var
that
=
this
;
superagent
.
post
(
'/api/tokens'
).
query
({
access_token
:
localStorage
.
accessToken
}).
end
(
function
(
error
,
result
)
{
if
(
error
&&
!
result
)
return
that
.
$message
.
error
(
error
.
message
);
that
.
refreshAccessTokens
();
});
},
onDeleteAccessToken
:
function
(
token
)
{
var
that
=
this
;
this
.
$confirm
(
'All actions from apps using this token will fail!'
,
'Really delete this access token?'
,
{
confirmButtonText
:
'Yes Delete'
,
cancelButtonText
:
'No'
}).
then
(
function
()
{
superagent
.
delete
(
'/api/tokens/'
+
token
).
query
({
access_token
:
localStorage
.
accessToken
}).
end
(
function
(
error
,
result
)
{
if
(
error
&&
!
result
)
return
that
.
$message
.
error
(
error
.
message
);
that
.
refreshAccessTokens
();
});
}).
catch
(
function
()
{});
},
prettyDate
:
function
(
row
,
column
,
cellValue
,
index
)
{
var
date
=
new
Date
(
cellValue
),
diff
=
(((
new
Date
()).
getTime
()
-
date
.
getTime
())
/
1000
),
...
...
server.js
View file @
c2c00fc
...
...
@@ -77,6 +77,9 @@ router.post ('/api/login', auth.login);
router
.
post
(
'/api/logout'
,
auth
.
verify
,
auth
.
logout
);
router
.
get
(
'/api/settings'
,
auth
.
verify
,
getSettings
);
router
.
put
(
'/api/settings'
,
auth
.
verify
,
setSettings
);
router
.
get
(
'/api/tokens'
,
auth
.
verify
,
auth
.
getTokens
);
router
.
post
(
'/api/tokens'
,
auth
.
verify
,
auth
.
createToken
);
router
.
delete
(
'/api/tokens/:token'
,
auth
.
verify
,
auth
.
delToken
);
router
.
get
(
'/api/profile'
,
auth
.
verify
,
auth
.
getProfile
);
router
.
get
(
'/api/files/*'
,
auth
.
verify
,
files
.
get
);
router
.
post
(
'/api/files/*'
,
auth
.
verify
,
multipart
,
files
.
post
);
...
...
src/auth.js
View file @
c2c00fc
...
...
@@ -15,6 +15,8 @@ const LDAP_USERS_BASE_DN = process.env.CLOUDRON_LDAP_USERS_BASE_DN;
const
LOCAL_AUTH_FILE
=
path
.
resolve
(
process
.
env
.
LOCAL_AUTH_FILE
||
'./.users.json'
);
const
TOKENSTORE_FILE
=
path
.
resolve
(
process
.
env
.
TOKENSTORE_FILE
||
'./.tokens.json'
);
const
AUTH_METHOD
=
(
LDAP_URL
&&
LDAP_USERS_BASE_DN
)
?
'ldap'
:
'local'
;
const
LOGIN_TOKEN_PREFIX
=
'login-'
;
const
API_TOKEN_PREFIX
=
'api-'
;
if
(
AUTH_METHOD
===
'ldap'
)
{
console
.
log
(
'Use ldap auth'
);
...
...
@@ -34,8 +36,11 @@ var tokenStore = {
get
:
function
(
token
,
callback
)
{
callback
(
tokenStore
.
data
[
token
]
?
null
:
'not found'
,
tokenStore
.
data
[
token
]);
},
set
:
function
(
token
,
data
,
callback
)
{
tokenStore
.
data
[
token
]
=
data
;
getApiTokens
:
function
(
callback
)
{
callback
(
null
,
Object
.
keys
(
tokenStore
.
data
).
filter
(
function
(
t
)
{
return
t
.
indexOf
(
API_TOKEN_PREFIX
)
===
0
;
}))
},
set
:
function
(
token
,
user
,
callback
)
{
tokenStore
.
data
[
token
]
=
user
;
tokenStore
.
save
();
callback
(
null
);
},
...
...
@@ -102,7 +107,7 @@ exports.login = function (req, res, next) {
verifyUser
(
req
.
body
.
username
,
req
.
body
.
password
,
function
(
error
,
user
)
{
if
(
error
)
return
next
(
new
HttpError
(
401
,
'Invalid credentials'
));
var
accessToken
=
uuid
();
var
accessToken
=
LOGIN_TOKEN_PREFIX
+
uuid
();
tokenStore
.
set
(
accessToken
,
user
,
function
(
error
)
{
if
(
error
)
return
next
(
new
HttpError
(
500
,
error
));
...
...
@@ -139,6 +144,32 @@ exports.getProfile = function (req, res, next) {
next
(
new
HttpSuccess
(
200
,
{
username
:
req
.
user
.
username
}));
};
exports
.
getTokens
=
function
(
req
,
res
,
next
)
{
tokenStore
.
getApiTokens
(
function
(
error
,
result
)
{
if
(
error
)
return
next
(
new
HttpError
(
500
,
error
));
next
(
new
HttpSuccess
(
200
,
{
accessTokens
:
result
}));
});
};
exports
.
createToken
=
function
(
req
,
res
,
next
)
{
var
accessToken
=
API_TOKEN_PREFIX
+
uuid
();
tokenStore
.
set
(
accessToken
,
req
.
user
,
function
(
error
)
{
if
(
error
)
return
next
(
new
HttpError
(
500
,
error
));
next
(
new
HttpSuccess
(
201
,
{
accessToken
:
accessToken
}));
});
};
exports
.
delToken
=
function
(
req
,
res
,
next
)
{
tokenStore
.
del
(
req
.
params
.
token
,
function
(
error
)
{
if
(
error
)
console
.
error
(
error
);
next
(
new
HttpSuccess
(
200
,
{}));
});
};
// webdav usermanager
exports
.
WebdavUserManager
=
WebdavUserManager
;
...
...
Write
Preview
Markdown
is supported
Attach a file
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 post a comment