Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Open sidebar
DNS Witch
Nomilo
Commits
76f222e1
Commit
76f222e1
authored
May 02, 2021
by
Gaël Berthaud-Müller
Browse files
change zone member management
parent
2a335445
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
111 additions
and
59 deletions
+111
-59
src/main.rs
src/main.rs
+2
-1
src/models/users.rs
src/models/users.rs
+57
-34
src/routes/users.rs
src/routes/users.rs
+0
-21
src/routes/zones.rs
src/routes/zones.rs
+52
-3
No files found.
src/main.rs
View file @
76f222e1
...
...
@@ -26,7 +26,8 @@ async fn rocket() -> rocket::Rocket {
.attach
(
DbConn
::
fairing
())
.mount
(
"/api/v1"
,
routes!
[
get_zone_records
,
create_user_zone
,
get_zones
,
add_member_to_zone
,
create_auth_token
,
create_user
])
...
...
src/models/users.rs
View file @
76f222e1
...
...
@@ -21,7 +21,6 @@ use crate::schema::*;
use
crate
::
DbConn
;
use
crate
::
config
::
Config
;
use
crate
::
models
::
errors
::{
ErrorResponse
,
make_500
};
use
crate
::
models
::
dns
::
AbsoluteName
;
const
BEARER
:
&
str
=
"Bearer "
;
...
...
@@ -38,7 +37,6 @@ pub enum Role {
}
// TODO: Store Uuid instead of string??
// TODO: Store role as Role and not String.
#[derive(Debug,
Queryable,
Identifiable,
Insertable)]
#[table_name
=
"user"
]
pub
struct
User
{
...
...
@@ -63,9 +61,10 @@ pub struct UserZone {
pub
zone_id
:
String
,
}
#[derive(Debug,
Queryable,
Identifiable,
Insertable)]
#[derive(Debug,
Serialize,
Queryable,
Identifiable,
Insertable)]
#[table_name
=
"zone"
]
pub
struct
Zone
{
#[serde(skip)]
pub
id
:
String
,
pub
name
:
String
,
}
...
...
@@ -79,8 +78,8 @@ pub struct CreateUserRequest {
}
#[derive(Debug,
Deserialize)]
pub
struct
CreateUserZone
Request
{
pub
zone
:
AbsoluteName
,
pub
struct
AddZoneMember
Request
{
pub
id
:
String
,
}
// pub struct LdapUserAssociation {
...
...
@@ -145,36 +144,16 @@ impl UserInfo {
Ok
(
res_zone
)
}
pub
fn
add
_zone
(
&
self
,
conn
:
&
diesel
::
SqliteConnection
,
request
:
CreateUserZoneRequest
)
->
Result
<
Zone
,
UserError
>
{
pub
fn
get
_zone
s
(
&
self
,
conn
:
&
diesel
::
SqliteConnection
)
->
Result
<
Vec
<
Zone
>
,
UserError
>
{
use
crate
::
schema
::
user_zone
::
dsl
::
*
;
use
crate
::
schema
::
zone
::
dsl
::
*
;
let
zone_name
=
request
.zone
.to_utf8
();
let
current_zone
:
Zone
=
zone
.filter
(
name
.eq
(
zone_name
))
.get_result
(
conn
)
.map_err
(|
e
|
match
e
{
DieselError
::
NotFound
=>
UserError
::
ZoneNotFound
,
other
=>
UserError
::
DbError
(
other
)
})
?
;
let
new_user_zone
=
UserZone
{
zone_id
:
current_zone
.id
.clone
(),
user_id
:
self
.id
.clone
()
};
let
res
=
diesel
::
insert_into
(
user_zone
)
.values
(
new_user_zone
)
.execute
(
conn
);
match
res
{
// If user has already access to the zone, safely ignore the conflit
// TODO: use 'on conflict do nothing' in postgres when we get there
Err
(
DieselError
::
DatabaseError
(
diesel
::
result
::
DatabaseErrorKind
::
UniqueViolation
,
_
))
=>
(),
Err
(
e
)
=>
return
Err
(
e
.into
()),
Ok
(
_
)
=>
()
};
let
res
:
Vec
<
(
Zone
,
UserZone
)
>
=
zone
.inner_join
(
user_zone
)
.filter
(
user_id
.eq
(
&
self
.id
))
.get_results
(
conn
)
.map_err
(
UserError
::
DbError
)
?
;
Ok
(
current_zone
)
Ok
(
res
.into_iter
()
.map
(|(
z
,
_
)|
z
)
.collect
()
)
}
}
...
...
@@ -211,8 +190,8 @@ impl<'r> FromRequest<'r> for UserInfo {
let
user_id
=
token_data
.sub
;
conn
.run
(|
c
|
{
match
LocalUser
::
get_user_by_uuid
(
c
,
user_id
)
{
conn
.run
(
move
|
c
|
{
match
LocalUser
::
get_user_by_uuid
(
c
,
&
user_id
)
{
Err
(
UserError
::
NotFound
)
=>
ErrorResponse
::
from
(
UserError
::
NotFound
)
.into
(),
Err
(
e
)
=>
ErrorResponse
::
from
(
e
)
.into
(),
Ok
(
d
)
=>
Outcome
::
Success
(
d
),
...
...
@@ -318,7 +297,7 @@ impl LocalUser {
})
}
pub
fn
get_user_by_uuid
(
conn
:
&
diesel
::
SqliteConnection
,
request_user_id
:
String
)
->
Result
<
UserInfo
,
UserError
>
{
pub
fn
get_user_by_uuid
(
conn
:
&
diesel
::
SqliteConnection
,
request_user_id
:
&
str
)
->
Result
<
UserInfo
,
UserError
>
{
use
crate
::
schema
::
localuser
::
dsl
::
*
;
use
crate
::
schema
::
user
::
dsl
::
*
;
...
...
@@ -365,3 +344,47 @@ impl AuthClaims {
encode
(
&
Header
::
default
(),
&
self
,
&
EncodingKey
::
from_secret
(
secret
.as_ref
()))
}
}
// NOTE: Should probably not be implemented here
// also, "UserError" seems like a misleading name
impl
Zone
{
pub
fn
get_all
(
conn
:
&
diesel
::
SqliteConnection
)
->
Result
<
Vec
<
Zone
>
,
UserError
>
{
use
crate
::
schema
::
zone
::
dsl
::
*
;
zone
.get_results
(
conn
)
.map_err
(
UserError
::
DbError
)
}
pub
fn
get_by_name
(
conn
:
&
diesel
::
SqliteConnection
,
zone_name
:
&
str
)
->
Result
<
Zone
,
UserError
>
{
use
crate
::
schema
::
zone
::
dsl
::
*
;
zone
.filter
(
name
.eq
(
zone_name
))
.get_result
(
conn
)
.map_err
(|
e
|
match
e
{
DieselError
::
NotFound
=>
UserError
::
ZoneNotFound
,
other
=>
UserError
::
DbError
(
other
)
})
}
pub
fn
add_member
(
&
self
,
conn
:
&
diesel
::
SqliteConnection
,
new_member
:
&
UserInfo
)
->
Result
<
(),
UserError
>
{
use
crate
::
schema
::
user_zone
::
dsl
::
*
;
let
new_user_zone
=
UserZone
{
zone_id
:
self
.id
.clone
(),
user_id
:
new_member
.id
.clone
()
};
let
res
=
diesel
::
insert_into
(
user_zone
)
.values
(
new_user_zone
)
.execute
(
conn
);
match
res
{
// If user has already access to the zone, safely ignore the conflit
// TODO: use 'on conflict do nothing' in postgres when we get there
Err
(
DieselError
::
DatabaseError
(
diesel
::
result
::
DatabaseErrorKind
::
UniqueViolation
,
_
))
=>
(),
Err
(
e
)
=>
return
Err
(
e
.into
()),
Ok
(
_
)
=>
()
};
Ok
(())
}
}
src/routes/users.rs
View file @
76f222e1
...
...
@@ -6,10 +6,8 @@ use crate::config::Config;
use
crate
::
DbConn
;
use
crate
::
models
::
errors
::{
ErrorResponse
,
make_500
};
use
crate
::
models
::
users
::{
UserInfo
,
LocalUser
,
CreateUserRequest
,
CreateUserZoneRequest
,
AuthClaims
,
AuthTokenRequest
,
AuthTokenResponse
...
...
@@ -45,22 +43,3 @@ pub async fn create_user<'r>(conn: DbConn, user_request: Json<CreateUserRequest>
.status
(
Status
::
Created
)
.ok
()
}
#[post(
"/users/<user_id>/zones"
,
data
=
"<user_zone_request>"
)]
pub
async
fn
create_user_zone
<
'r
>
(
conn
:
DbConn
,
user_info
:
Result
<
UserInfo
,
ErrorResponse
>
,
user_id
:
String
,
user_zone_request
:
Json
<
CreateUserZoneRequest
>
)
->
Result
<
Response
<
'r
>
,
ErrorResponse
>
{
user_info
?
.check_admin
()
?
;
conn
.run
(
move
|
c
|
{
let
user_info
=
LocalUser
::
get_user_by_uuid
(
&
c
,
user_id
)
?
;
user_info
.add_zone
(
&
c
,
user_zone_request
.into_inner
())
})
.await
?
;
Response
::
build
()
.status
(
Status
::
Created
)
.ok
()
}
src/routes/zones.rs
View file @
76f222e1
use
rocket
::
Response
;
use
rocket
::
http
::
Status
;
use
rocket_contrib
::
json
::
Json
;
...
...
@@ -8,7 +9,7 @@ use trust_dns_client::rr::{DNSClass, RecordType};
use
crate
::{
DbConn
,
models
::
dns
};
use
crate
::
models
::
errors
::{
ErrorResponse
,
make_500
};
use
crate
::
models
::
users
::
UserInfo
;
use
crate
::
models
::
users
::
{
LocalUser
,
UserInfo
,
Zone
,
AddZoneMemberRequest
}
;
#[get(
"/zones/<zone>/records"
)]
...
...
@@ -24,7 +25,7 @@ pub async fn get_zone_records(
if
!
user_info
.is_admin
()
{
let
zone_name
=
zone
.clone
()
.to_string
();
conn
.run
(
move
|
c
|
{
dbg!
(
user_info
.get_zone
(
c
,
&
zone_name
)
)
user_info
.get_zone
(
c
,
&
zone_name
)
})
.await
?
;
}
...
...
@@ -33,10 +34,12 @@ pub async fn get_zone_records(
query
.await
.map_err
(
make_500
)
?
};
// TODO: Better error handling (ex. not authorized should be 500)
if
response
.response_code
()
!=
ResponseCode
::
NoError
{
println!
(
"Querrying of zone {} failed with code {}"
,
*
zone
,
response
.response_code
());
return
ErrorResponse
::
new
(
Status
::
NotFound
,
format!
(
"
z
one {} could not be found"
,
*
zone
)
format!
(
"
Z
one {} could not be found"
,
*
zone
)
)
.err
()
}
...
...
@@ -51,3 +54,49 @@ pub async fn get_zone_records(
Ok
(
Json
(
records
))
}
// TODO: the post version of that
#[get(
"/zones"
)]
pub
async
fn
get_zones
(
conn
:
DbConn
,
user_info
:
Result
<
UserInfo
,
ErrorResponse
>
,
)
->
Result
<
Json
<
Vec
<
Zone
>>
,
ErrorResponse
>
{
let
user_info
=
user_info
?
;
let
zones
=
conn
.run
(
move
|
c
|
{
if
user_info
.is_admin
()
{
Zone
::
get_all
(
c
)
}
else
{
user_info
.get_zones
(
c
)
}
})
.await
?
;
Ok
(
Json
(
zones
))
}
#[post(
"/zones/<zone>/members"
,
data
=
"<zone_member_request>"
)]
pub
async
fn
add_member_to_zone
<
'r
>
(
conn
:
DbConn
,
zone
:
dns
::
AbsoluteName
,
user_info
:
Result
<
UserInfo
,
ErrorResponse
>
,
zone_member_request
:
Json
<
AddZoneMemberRequest
>
)
->
Result
<
Response
<
'r
>
,
ErrorResponse
>
{
let
user_info
=
user_info
?
;
let
zone_name
=
zone
.to_utf8
();
conn
.run
(
move
|
c
|
{
let
zone
=
if
user_info
.is_admin
()
{
Zone
::
get_by_name
(
c
,
&
zone_name
)
}
else
{
user_info
.get_zone
(
c
,
&
zone_name
)
}
?
;
let
new_member
=
LocalUser
::
get_user_by_uuid
(
c
,
&
zone_member_request
.id
)
?
;
zone
.add_member
(
&
c
,
&
new_member
)
})
.await
?
;
Response
::
build
()
.status
(
Status
::
Created
)
// TODO: change this?
.ok
()
}
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