Skip to content

Commit

Permalink
fix issue #45
Browse files Browse the repository at this point in the history
  • Loading branch information
th-schwarz committed Aug 25, 2024
1 parent af45786 commit 4a53aea
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 34 deletions.
12 changes: 6 additions & 6 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ _mydyndns.domain.com_ by calling the following curl command:
[source,console]
----
curl -u dyndns:test123 -i \
"http://localhost:8081/update/mydyndns.domain.com?apitoken=1234567890abcdf&ipv4=127.1.2.4&ipv6=2a03:4000:41:32::2"
"http://localhost:8081/router/mydyndns.domain.com?apitoken=1234567890abcdf&ipv4=127.1.2.4&ipv6=2a03:4000:41:32::2"
----

For each host an api-token must be defined. If the api-token doesn’t
For each host an api-token must be defined. If the api-token does not
match the host, the update will be failed. Due to this security feature,
DynDRest can be used by different people. They can’t update the IPs each
other.
Expand All @@ -84,8 +84,8 @@ Here is the suggested file structure:
----
├── /opt/dyndrest
│ ├── dyndrest.yml
│ ├── dyndrest-0.7.0.jar
│ ├── dyndrest.jar -> dyndrest-0.7.0.jar
│ ├── dyndrest-0.8.0.jar
│ ├── dyndrest.jar -> dyndrest-0.8.0.jar
│ ├── logback.xml (logback configuration)
│ ├── dyndrest.mv.db (h2 database file)
│ ├── /backup
Expand Down Expand Up @@ -209,14 +209,14 @@ It is enabled by default, but it can be disabled by setting the property

As an example, let’s have a look at the setup of dynamic DNS in the
https://service.avm.de/help/en/FRITZ-Box-7530/019p2/hilfe_dyndns[Fritz!Box
7590]. The following settngs are required:
7590]. The following settings are required:

* _DynDNS Provider:_
User-defined
* _Domain name:_ The hostname for which the IPs should be
updated.
* _Username / Password:_ The credentials for basic-auth.
* _Update-URL:_
[your-host:port]/update/<domain>?apitoken=[yourApitoken]&ipv4=<ipaddr>&ipv6=<ip6addr>
[your-host:port]/router/<domain>?apiToken=[yourApiToken]&ipv4=<ipaddr>&ipv6=<ip6addr>
If both IP parameters are omitted, an attempt is made to fetch the
remote IP.
79 changes: 69 additions & 10 deletions docs/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,20 @@ paths:
type: string
description: "The 'apiToken', which must belong to the host'."
responses:
"403":
description: "If the 'apiToken' doesn't belong to the host, IP addresses\
\ aren't valid or the remote IP couldn't determine."
"404":
description: If the 'host' isn't configured.
"200":
description: A JSON object with the IP settings of the 'host'.
content:
application/json:
example:
ipv4: 127.1.2.4
ipv6: 2a03:4000:41:32:0:0:0:2
"403":
description: "If the 'apiToken' doesn't belong to the host, IP addresses\
\ aren't valid or the remote IP couldn't determine."
"500":
description: If the zone info fails.
"404":
description: If the 'host' isn't configured.
put:
tags:
- api-controller
Expand Down Expand Up @@ -168,12 +168,13 @@ paths:
example: domain.com
- name: host
in: path
description: "The name of the host to create, It is just the sub-domain part."
description: "The name of the host to create, it's just the sub-domain part."
required: true
schema:
type: string
description: "The name of the host to create, It is just the sub-domain\
\ part."
description: "The name of the host to create, it's just the sub-domain part."
example: host1
example: host1
- name: apiToken
in: query
description: The 'api-token' to authenticate IP changes of the created host.
Expand Down Expand Up @@ -201,6 +202,64 @@ paths:
description: The 'adminToken' is wrong.
"404":
description: The zone doesn't exists.
/router/{host}:
get:
tags:
- api-controller
summary: "Updates the desired IP addresses of the 'host'. If both parameters\
\ for IP addresses aren't set, an attempt is made to fetch the remote IP.\
\ It is an alternative route for routers which requires the GET method!"
operationId: routerUpdateHost
parameters:
- name: host
in: path
description: "The host, for which the IPs must be updated. It has to be a\
\ full domain name."
required: true
schema:
type: string
description: "The host, for which the IPs must be updated. It has to be\
\ a full domain name."
example: mydyndns.domain.com
example: mydyndns.domain.com
- name: apiToken
in: query
description: The 'apiToken' to authenticate the changes of the IPs for this
host.
required: true
schema:
type: string
description: The 'apiToken' to authenticate the changes of the IPs for this
host.
- name: ipv4
in: query
description: An IPv4 address.
required: false
schema:
type: string
description: An IPv4 address.
- name: ipv6
in: query
description: An IPv6 address.
required: false
schema:
type: string
description: An IPv6 address.
responses:
"200":
description: The IPs are still up to date.
"400":
description: At least one IP address isn't valid or the remote IP couldn't
be determined.
"201":
description: 'One or both IPs are changed. Update successful processed.
(Dependent on the configuration the response code could be ''200''! '
"403":
description: If the 'apiToken' doesn't belong to the host.
"404":
description: The desired host doesn't exists.
"500":
description: If the update failed.
/admin/zones:
get:
tags:
Expand Down Expand Up @@ -250,6 +309,8 @@ paths:
type: string
description: The 'admin-token' to authorize the operation.
responses:
"403":
description: The 'adminToken' is wrong.
"200":
description: The hosts of the desired zones fetched successful.
content:
Expand All @@ -261,8 +322,6 @@ paths:
ns: a1.nameserver.net
fullHost: master.mydomain.net
changed: 2024-03-11T09:07:59.037688
"403":
description: The 'adminToken' is wrong.
/admin/zones/{name}:
delete:
tags:
Expand Down
55 changes: 37 additions & 18 deletions src/main/java/codes/thischwa/dyndrest/ApiController.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/** The 'main' api controller. */
@RestController
@Slf4j
public class ApiController implements ApiRoutes {
public class ApiController implements ApiRoutes, RouterRoutes {

private final Provider provider;

Expand Down Expand Up @@ -62,8 +62,44 @@ public ResponseEntity<Void> updateHost(
HttpServletRequest req) {
log.debug(
"entered #update: host={}, apiToken={}, ipv4={}, ipv6={}", host, apiToken, ipv4, ipv6);
return updateIpAddresses(host, apiToken, ipv4, ipv6, req);
}

@Override
public ResponseEntity<IpSetting> fetchHostIpSetting(String host, @RequestParam String apiToken) {
log.debug("entered #info: host={}", host);
// validation
validateHost(host, apiToken);

IpSetting ipSetting;
try {
ipSetting = provider.info(host);
} catch (ProviderException e) {
log.error("Zone info failed for: " + host, e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return ResponseEntity.ok(ipSetting);
}

@Override
public ResponseEntity<Void> routerUpdateHost(
String host, String apiToken, InetAddress ipv4, InetAddress ipv6, HttpServletRequest req) {
log.debug(
"entered #routerUpdateHost: host={}, apiToken={}, ipv4={}, ipv6={}",
host,
apiToken,
ipv4,
ipv6);
return updateIpAddresses(host, apiToken, ipv4, ipv6, req);
}

private ResponseEntity<Void> updateIpAddresses(
String host,
String apiToken,
@Nullable InetAddress ipv4,
@Nullable InetAddress ipv6,
HttpServletRequest req) {
validateHost(host, apiToken);
IpSetting reqIpSetting = new IpSetting(ipv4, ipv6);
if (reqIpSetting.isNotSet()) {
log.debug("Both IP parameters are null, try to fetch the remote IP.");
Expand Down Expand Up @@ -101,23 +137,6 @@ public ResponseEntity<Void> updateHost(
return ResponseEntity.ok().build();
}

@Override
public ResponseEntity<IpSetting> fetchHostIpSetting(String host, @RequestParam String apiToken) {
log.debug("entered #info: host={}", host);
// validation
validateHost(host, apiToken);

IpSetting ipSetting;
try {
ipSetting = provider.info(host);
} catch (ProviderException e) {
log.error("Zone info failed for: " + host, e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

return ResponseEntity.ok(ipSetting);
}

private void validateHost(String host, String apiToken) {
try {
boolean valid = hostZoneService.validate(host, apiToken);
Expand Down
60 changes: 60 additions & 0 deletions src/main/java/codes/thischwa/dyndrest/RouterRoutes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package codes.thischwa.dyndrest;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

public interface RouterRoutes {

@Operation(
summary =
"Updates the desired IP addresses of the 'host'. If both parameters for IP addresses aren't set, an attempt is made to fetch the remote IP. It is an alternative route for routers which requires the GET method!")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "The IPs are still up to date."),
@ApiResponse(
responseCode = "201",
description =
"One or both IPs are changed. Update successful processed. (Dependent on the configuration the response code could be '200'! "),
@ApiResponse(
responseCode = "400",
description =
"At least one IP address isn't valid or the remote IP couldn't be determined."),
@ApiResponse(
responseCode = "403",
description = "If the 'apiToken' doesn't belong to the host."),
@ApiResponse(responseCode = "404", description = "The desired host doesn't exists."),
@ApiResponse(responseCode = "500", description = "If the update failed.")
})
@GetMapping(value = "/router/{host}")
ResponseEntity<Void> routerUpdateHost(
@Schema(
description =
"The host, for which the IPs must be updated. It has to be a full domain name.",
type = "string",
example = "mydyndns.domain.com")
@PathVariable
String host,
@Schema(
description = "The 'apiToken' to authenticate the changes of the IPs for this host.",
type = "string")
@RequestParam
String apiToken,
@Schema(description = "An IPv4 address.", type = "string", examples = "127.1.2.4")
@RequestParam(name = "ipv4", required = false)
InetAddress ipv4,
@Schema(
description = "An IPv6 address.",
type = "string",
examples = "2a03:4000:41:32:0:0:0:2")
@RequestParam(name = "ipv6", required = false)
InetAddress ipv6,
HttpServletRequest req);
}

0 comments on commit 4a53aea

Please sign in to comment.