Completed
Push — master ( bfae73...c3f7bd )
by leo
04:23
created

ValidateController::v3ValidateAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: chenyihong
5
 * Date: 16/8/1
6
 * Time: 14:52
7
 */
8
9
namespace Leo108\CAS\Http\Controllers;
10
11
use Illuminate\Support\Str;
12
use Leo108\CAS\Contracts\TicketLocker;
13
use Leo108\CAS\Repositories\TicketRepository;
14
use Leo108\CAS\Exceptions\CAS\CasException;
15
use Leo108\CAS\Models\Ticket;
16
use Illuminate\Http\Request;
17
use Illuminate\Http\Response;
18
use SimpleXMLElement;
19
20
class ValidateController extends Controller
21
{
22
    /**
23
     * @var TicketLocker
24
     */
25
    protected $ticketLocker;
26
    /**
27
     * @var TicketRepository
28
     */
29
    protected $ticketRepository;
30
31
    const BASE_XML = '<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"></cas:serviceResponse>';
32
33
    /**
34
     * ValidateController constructor.
35
     * @param TicketLocker     $ticketLocker
36
     * @param TicketRepository $ticketRepository
37
     */
38 4
    public function __construct(TicketLocker $ticketLocker, TicketRepository $ticketRepository)
39
    {
40 4
        $this->ticketLocker     = $ticketLocker;
41 4
        $this->ticketRepository = $ticketRepository;
42 4
    }
43
44 1
    public function v1ValidateAction(Request $request)
45
    {
46 1
        $service = $request->get('service', '');
47 1
        $ticket  = $request->get('ticket', '');
48 1
        if (empty($service) || empty($ticket)) {
49 1
            return new Response('no');
50
        }
51
52 1
        if (!$this->lockTicket($ticket)) {
53 1
            return new Response('no');
54
        }
55 1
        $record = $this->ticketRepository->getByTicket($ticket);
56 1
        if (!$record || $record->service_url != $service) {
57 1
            $this->unlockTicket($ticket);
58
59 1
            return new Response('no');
60
        }
61 1
        $this->ticketRepository->invalidTicket($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->ticketRepository->getByTicket($ticket) on line 55 can also be of type boolean; however, Leo108\CAS\Repositories\...sitory::invalidTicket() does only seem to accept object<Leo108\CAS\Models\Ticket>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
62
63 1
        $this->unlockTicket($ticket);
64
65 1
        return new Response('yes');
66
    }
67
68 1
    public function v2ValidateAction(Request $request)
69
    {
70 1
        return $this->casValidate($request, false);
71
    }
72
73 1
    public function v3ValidateAction(Request $request)
74
    {
75 1
        return $this->casValidate($request, true);
76
    }
77
78
    /**
79
     * @param Request $request
80
     * @param bool    $returnAttr
81
     * @return Response
82
     */
83 1
    protected function casValidate(Request $request, $returnAttr)
84
    {
85 1
        $service = $request->get('service', '');
86 1
        $ticket  = $request->get('ticket', '');
87 1
        $format  = strtoupper($request->get('format', 'XML'));
88 1
        if (empty($service) || empty($ticket)) {
89 1
            return $this->failureResponse(
90 1
                CasException::INVALID_REQUEST,
91 1
                'param service and ticket can not be empty',
92
                $format
93 1
            );
94
        }
95
96 1
        if (!$this->lockTicket($ticket)) {
97 1
            return $this->failureResponse(CasException::INTERNAL_ERROR, 'try to lock ticket failed', $format);
98
        }
99
100 1
        $record = $this->ticketRepository->getByTicket($ticket);
101
        try {
102 1
            if (!$record) {
103 1
                throw new CasException(CasException::INVALID_TICKET, 'ticket is not valid');
104
            }
105
106 1
            if ($record->service_url != $service) {
107 1
                throw new CasException(CasException::INVALID_SERVICE, 'service is not valid');
108
            }
109 1
        } catch (CasException $e) {
110
            //invalid ticket if error occur
111 1
            $record instanceof Ticket && $this->ticketRepository->invalidTicket($record);
112 1
            $this->unlockTicket($ticket);
113
114 1
            return $this->failureResponse($e->getCasErrorCode(), $e->getMessage(), $format);
115
        }
116 1
        $this->ticketRepository->invalidTicket($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->ticketRepository->getByTicket($ticket) on line 100 can also be of type boolean; however, Leo108\CAS\Repositories\...sitory::invalidTicket() does only seem to accept object<Leo108\CAS\Models\Ticket>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
117 1
        $this->unlockTicket($ticket);
118
119 1
        $attr = $returnAttr ? $record->user->getCASAttributes() : [];
120
121 1
        return $this->successResponse($record->user->getName(), $attr, $format);
122
    }
123
124
    /**
125
     * @param string $username
126
     * @param array  $attrs
127
     * @param string $format
128
     * @return Response
129
     */
130 1
    protected function successResponse($username, $attrs, $format)
131
    {
132 1
        if (strtoupper($format) === 'JSON') {
133
            $data = [
134
                'serviceResponse' => [
135
                    'authenticationSuccess' => [
136 1
                        'user' => $username,
137 1
                    ],
138 1
                ],
139 1
            ];
140
141 1
            if (!empty($attrs)) {
142 1
                $data['serviceResponse']['authenticationSuccess']['attributes'] = $attrs;
143 1
            }
144
145 1
            return new Response($data);
146
        } else {
147 1
            $xml          = simplexml_load_string(self::BASE_XML);
148 1
            $childSuccess = $xml->addChild('cas:authenticationSuccess');
149 1
            $childSuccess->addChild('cas:user', $username);
150
151 1
            if (!empty($attrs)) {
152 1
                $childAttrs = $childSuccess->addChild('cas:attributes');
153 1
                foreach ($attrs as $key => $value) {
154 1
                    if (is_string($value)) {
155 1
                        $str = $value;
156 1
                    } else if (is_object($value) && method_exists($value, '__toString')) {
157 1
                        $str = $value->__toString();
158 1
                    } else if ($value instanceof \Serializable) {
159 1
                        $str = serialize($value);
160 1
                    } else {
161
                        //array or object that doesn't have __toString method
162
                        //json_encode will return false if encode failed
163 1
                        $str = json_encode($value);
164
                    }
165
166 1
                    if (is_string($str)) {
167 1
                        $childAttrs->addChild('cas:'.$key, $str);
168 1
                    }
169 1
                }
170 1
            }
171
172 1
            return $this->returnXML($xml);
173
        }
174
    }
175
176
    /**
177
     * @param string $code
178
     * @param string $desc
179
     * @param string $format
180
     * @return Response
181
     */
182 1
    protected function failureResponse($code, $desc, $format)
183
    {
184 1
        if (strtoupper($format) === 'JSON') {
185 1
            return new Response(
186
                [
187
                    'serviceResponse' => [
188
                        'authenticationFailure' => [
189 1
                            'code'        => $code,
190 1
                            'description' => $desc,
191 1
                        ],
192 1
                    ],
193
                ]
194 1
            );
195
        } else {
196 1
            $xml          = simplexml_load_string(self::BASE_XML);
197 1
            $childFailure = $xml->addChild('cas:authenticationFailure', $desc);
198 1
            $childFailure->addAttribute('code', $code);
199
200 1
            return $this->returnXML($xml);
201
        }
202
    }
203
204
    /**
205
     * @param string $ticket
206
     * @return bool
207
     */
208 1
    protected function lockTicket($ticket)
209
    {
210 1
        return $this->ticketLocker->acquireLock($ticket, config('cas.lock_timeout'));
211
    }
212
213
    /**
214
     * @param string $ticket
215
     * @return bool
216
     */
217 1
    protected function unlockTicket($ticket)
218
    {
219 1
        return $this->ticketLocker->releaseLock($ticket);
220
    }
221
222
    /**
223
     * remove the first line of xml string
224
     * @param string $str
225
     * @return string
226
     */
227 1
    protected function removeXmlFirstLine($str)
228
    {
229 1
        $first = '<?xml version="1.0"?>';
230 1
        if (Str::startsWith($str, $first)) {
231 1
            return trim(substr($str, strlen($first)));
232
        }
233
234 1
        return $str;
235
    }
236
237
    /**
238
     * @param SimpleXMLElement $xml
239
     * @return Response
240
     */
241 1
    protected function returnXML(SimpleXMLElement $xml)
242
    {
243 1
        return new Response($this->removeXmlFirstLine($xml->asXML()), 200, array('Content-Type' => 'application/xml'));
0 ignored issues
show
Security Bug introduced by
It seems like $xml->asXML() targeting SimpleXMLElement::asXML() can also be of type false; however, Leo108\CAS\Http\Controll...r::removeXmlFirstLine() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
244
    }
245
}