package Gogs; # ABSTRACT: Subclass of Pithub use strict; use warnings; use Moo; use MIME::Base64; #use Gogs::Repos; extends 'Pithub'; =head1 DESCRIPTION L is an acceptable API client for Gogs, since their APIs are compatible. However the two have a number of differences, so they must be accounted for. The most important of which being that all requests require an API token. It is the caller's responsibility to know ahead of time whether the server is gogs or not. =head1 METHODS =head2 get_token(%options) We can simplify tooling around this to not require you enter the "Applications" section of the gogs interface. Requires that the object is instantiated with a username, that you pass a password, and that the api_uri is secure unless you pass the 'insecure' option. If the named token exists already it will delete and re-create the named token. It is HIGHLY RECOMMENDED you delete the token at the end of your session with the delete_token() method. =cut # All API access requires a token on Gogs EXCEPT users/$user/tokens. # As such, I'm restricting every call not ending in /tokens my @TOKEN_REQUIRED_REGEXP = (qr{(?!/tokens$)}); sub _validate_tok_args { my ($self,%options) = @_; die "Must instantiate with username" unless $self->user; die "Must pass token name or sha1" unless $options{name} || $options{sha1}; die "Refuse to operate over insecure connections" if !$options{insecure} && _insecure($self->api_uri); die "Must provide password to fetch tokens" unless $options{password}; } sub _build_tok_headers { my ($self, %options) = @_; return ( 'Authorization' => "Basic ".encode_base64($self->user.":$options{password}"), 'Content-type' => "application/json", ); } sub get_token { my ($self, %options) = @_; # unset the token if passed, so that we use simple auth my $tok = $self->token(); $self->token("") if $tok; $self->_validate_tok_args(%options); my %auth_headers = $self->_build_tok_headers(%options); my $token_endpoint = "/users/".$self->user."/tokens"; my %req = ( method => 'GET', path => $token_endpoint, headers => \%auth_headers, ); my $result = $self->request(%req); my $content = $result->content(); die "Got bad response from server" unless ref $content eq "ARRAY"; my $token_actual; foreach my $token (@$content) { # If it exists, we have no means of deleteing it unless we actually have the real SHA1 (the one in list is not real). # As such just die. die "Token with name $options{name} already exists, cannot continue. Please delete it manually." if $token->{name} eq $options{name}; } # No such token named, so let's just make one. $req{method} = 'POST'; $req{data} = qq|{ "name":"$options{name}" }|; $result = $self->request(%req); $content = $result->content(); # Set the token again, since we are done with the requests $self->token($tok) if $tok; die "Got bad response from server" unless ref $content eq "HASH"; return $content->{sha1}; } =head2 delete_token(%options) Delete the token identified by the provided sha1 in %options. Should return a token name. Dies when unsuccessful. =cut sub delete_token { my ($self, %options) = @_; # unset the token if passed, so that we use simple auth my $tok = $self->token(); $self->token("") if $tok; $self->_validate_tok_args(%options); my %auth_headers = $self->_build_tok_headers(%options); my %req = ( method => 'DELETE', path => "/users/".$self->user."/tokens", headers => \%auth_headers, ); $req{data} = qq|{ "sha1":"$options{sha1}" }|; my $result = $self->request(%req); # Set the token again, since we are done with the requests $self->token($tok) if $tok; die "Got bad response from server on DELETE of token" unless $result && $result->response->is_success; return $result; } sub _insecure { my $uri = shift; return $uri =~ m/^http:\/\//; } 1;