File Coverage

blib/lib/WebService/CastleIO.pm
Criterion Covered Total %
statement 15 17 88.2
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 21 23 91.3


line stmt bran cond sub pod time code
1             package WebService::CastleIO;
2              
3 1     1   13506 use 5.10.0;
  1         3  
4 1     1   5 use strict;
  1         2  
  1         16  
5 1     1   4 use warnings;
  1         4  
  1         34  
6 1     1   8 use feature 'switch';
  1         8  
  1         115  
7 1     1   8 use feature 'say';
  1         2  
  1         32  
8              
9 1     1   275 use JSON;
  0            
  0            
10             use REST::Client;
11             use MIME::Base64;
12              
13             use Moose;
14             use Moose::Util::TypeConstraints;
15             use MooseX::Params::Validate;
16              
17              
18             =head1 NAME
19              
20             WebService::CastleIO - Castle.io API client
21              
22              
23             =head1 VERSION
24              
25             Version 1.01
26              
27             =cut
28              
29              
30             our $VERSION = '1.02';
31              
32              
33              
34             has api_secret => (
35             is => 'rw',
36             isa => 'Str'
37             );
38              
39             has api_url => (
40             is => 'ro',
41             isa => 'Str',
42             default => 'https://api.castle.io/v1'
43             );
44              
45             has format => (
46             is => 'ro',
47             isa => 'Str',
48             default => 'json'
49             );
50              
51             has cookie_id => (
52             is => 'rw',
53             isa => 'Str'
54             );
55              
56             has ip_address => (
57             is => 'rw',
58             isa => 'Str'
59             );
60              
61             has headers => (
62             is => 'rw',
63             isa => 'Str',
64             );
65              
66             has source => (
67             is => 'rw',
68             isa => 'Str'
69             );
70              
71             has debug => (
72             is => 'ro',
73             isa => 'Bool',
74             default => 0
75             );
76              
77              
78              
79             =head1 SYNOPSIS
80              
81             Castle detects and mitigates account takeover in web and mobile apps. This is a third party API client built for the Castle.io API.
82              
83              
84             =head1 CONFIGURATION
85            
86             use WebService::CastleIO;
87            
88             my $castle = WebService::CastleIO->new(
89             api_secret => 'sRq3Zmzpxwu6eDXiYCFB3xyfi4ZnVjnn',
90             cookie_id => 'abcd',
91             ip_address => '24.61.128.172',
92             headers => JSON->new->allow_nonref->utf8->encode({'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0'}),
93             source => 'backend',
94             debug => 1
95             );
96              
97              
98             =head1 SUBROUTINES/METHODS
99              
100              
101             =head2 track
102              
103             Track lets you record security-related actions your users perform. Events are processed
104             asynchronously to return as quickly as possible.
105              
106             my $event_result = $castle->track(
107             data => {user_id => 'dummy', name => '$login.succeeded', properties => {threat => 'Large', whatever => 'made up'}}
108             );
109              
110             =cut
111              
112             sub track {
113             my ($self, %params) = validated_hash(
114             \@_,
115             data => {isa => 'Maybe[HashRef]'},
116             );
117              
118             say '[track] Create new event' if $self->debug;
119              
120             $self->_call(endpoint => 'track', args => \%params, method => 'POST');
121             }
122              
123              
124              
125             =head2 authenticate
126              
127             Authenticate is processed synchronous and returns returns an action of the types approve, challenge or deny.
128              
129             my $auth_result = $castle->authenticate(
130             data => {user_id => 'dummywriter', name => '$login.succeeded'},
131             );
132              
133              
134             =cut
135              
136             sub authenticate {
137             my ($self, %params) = validated_hash(
138             \@_,
139             data => {isa => 'Maybe[HashRef]'},
140             );
141              
142             say '[authenticate] Authenticate user' if $self->debug;
143              
144             $self->_call(endpoint => 'authenticate', args => \%params, method => 'POST');
145             }
146              
147              
148              
149              
150             =head2 identify
151              
152             User updates are processed asynchronously to return as quickly as possible.
153              
154             my $identify_result = $castle->identify(
155             data => {user_id => 'dummywriter', traits => {'foo' => 'bar'}},
156             );
157              
158             =cut
159              
160             sub identify {
161             my ($self, %params) = validated_hash(
162             \@_,
163             data => {isa => 'Maybe[HashRef]'},
164             );
165              
166             say '[identify] Identify user' if $self->debug;
167              
168             $self->_call(endpoint => 'identify', args => \%params, method => 'POST');
169             }
170              
171              
172              
173             =head2 review
174              
175             Reviews lets you fetch the context for a user to review anomalous account activity.
176              
177             my $reviews_result = $castle->review(review_id => 12356789);
178              
179             =cut
180              
181             sub review {
182             my ($self, %params) = validated_hash(
183             \@_,
184             review_id => {isa => 'Int'}
185             );
186              
187             say '[review] Review user activity' if $self->debug;
188              
189             $self->_call(endpoint => "reviews/$params{review_id}", args => \%params, method => 'GET');
190             }
191              
192              
193              
194             =head2 _call
195              
196             Private method that makes call to API web service.
197              
198             =cut
199              
200             sub _call {
201             my ($self, %params) = validated_hash(
202             \@_,
203             endpoint => {isa => 'Str'},
204             args => {isa => 'Maybe[HashRef]'},
205             method => {isa => enum([qw(POST GET)])}
206             );
207              
208             my $headers = {
209             'X-Castle-Ip' => $self->ip_address,
210             'X-Castle-Source' => $self->source,
211             'X-Castle-Cookie-Id' => $self->cookie_id,
212             'X-Castle-Headers' => $self->headers,
213             'X-Castle-Client-User-Agent' => $self->_client_user_agent,
214             'Content-Type' => 'application/json',
215             'User-Agent' => 'Castle API Client ' . $VERSION
216             };
217              
218             my $url = $self->api_url . '/' . $params{endpoint} . '.' . $self->format;
219              
220             my $client = REST::Client->new();
221             $client->addHeader("Authorization", "Basic " . encode_base64(':' . $self->api_secret, ''));
222              
223             for ($params{method}) {
224             when ('GET') {
225             say "[_call]: Making call GET $url" if $self->debug;
226             my $url_args = defined $params{args}{data} ? '?' . join('&', map {"$_=$params{args}{data}{$_}"} keys %{$params{args}{data}}) : '';
227             $url .= $url_args;
228             $client->GET($url);
229             }
230             when ('POST') {
231             my $json_args = JSON->new->allow_nonref->utf8->encode($params{args}{data});
232             say "[_call]: Making call POST $url" if $self->debug;
233             $client->POST($url, $json_args, $headers)
234             }
235             }
236              
237             $client->responseCode();
238             }
239              
240              
241              
242             =head2 _client_user_agent
243              
244             Private method to return user agent.
245              
246             =cut
247              
248             sub _client_user_agent {
249             JSON->new->allow_nonref->utf8->encode({
250             bindings_version => $VERSION,
251             lang => 'perl',
252             publisher => 'castle',
253             uname => `uname -a`
254             });
255             }
256              
257              
258            
259             =head2 _json_to_object
260              
261             Private method that converts JSON result to object
262              
263             =cut
264            
265             sub _json_to_object {
266             my $self = shift;
267             my $json = shift;
268             JSON->new->allow_nonref->utf8->decode($json);
269             }
270              
271              
272              
273              
274              
275             =head1 AUTHOR
276              
277             Dino Simone, C<< >>
278              
279             =head1 BUGS
280              
281             Please report any bugs or feature requests to C, or through
282             the web interface at L. I will be notified, and then you'll
283             automatically be notified of progress on your bug as I make changes.
284              
285              
286              
287              
288             =head1 SUPPORT
289              
290             You can find documentation for this module with the perldoc command.
291              
292             perldoc WebService::CastleIO
293              
294              
295             You can also look for information at:
296              
297             =over 4
298              
299             =item * RT: CPAN's request tracker (report bugs here)
300              
301             L
302              
303             =item * AnnoCPAN: Annotated CPAN documentation
304              
305             L
306              
307             =item * CPAN Ratings
308              
309             L
310              
311             =item * Search CPAN
312              
313             L
314              
315             =back
316              
317              
318             =head1 ACKNOWLEDGEMENTS
319              
320              
321             =head1 LICENSE AND COPYRIGHT
322              
323             Copyright 2017 Dino Simone - dinosimone.com
324              
325             This program is distributed under the MIT (X11) License:
326             L
327              
328             Permission is hereby granted, free of charge, to any person
329             obtaining a copy of this software and associated documentation
330             files (the "Software"), to deal in the Software without
331             restriction, including without limitation the rights to use,
332             copy, modify, merge, publish, distribute, sublicense, and/or sell
333             copies of the Software, and to permit persons to whom the
334             Software is furnished to do so, subject to the following
335             conditions:
336              
337             The above copyright notice and this permission notice shall be
338             included in all copies or substantial portions of the Software.
339              
340             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
341             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
342             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
343             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
344             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
345             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
346             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
347             OTHER DEALINGS IN THE SOFTWARE.
348              
349              
350             =cut
351              
352             1; # End of WebService::CastleIO